Respond to router relay if necessary on child routes

I am struggling to do this work, this is the big picture, I think, but I could not see an example of this or a solution.

Here is the current route I'm working on

/app/services/10/
  • in appselect current user account
  • to /serviceschoose him a list of available services
  • in /10receive small service details 10

So I do this to populate the repository with some data:

application

import Services from './routes/Services'

export default (store) => ({
  path: 'main',
  getComponent (nextState, cb) {
    require.ensure([], require => {
      const App        = require('./containers/AppContainer').default,
            userActions = require('../store/user').actions
      store.dispatch(userActions.fetch())

      cb(null, App)
    }, 'app')
  },
  childRoutes: [
    Services(store)
  ]
})

Services

Now the problem is childRoutes:

import { injectReducer } from '../../../../store/reducers'
import Manage from './routes/Manage'

export default (store) => ({
  path: 'services',
  getComponent (nextState, cb) {
    require.ensure([], require => {
      const Services = require('./containers/ServicesContainer').default
      const actions      = require('./modules/services').actions
      const reducer      = require('./modules/services').default
      store.dispatch(actions.fetchAll())
      injectReducer(store, { key: 'services', reducer })
      cb(null, Services)
    })
  },
  childRoutes: [
    Manage(store)
  ]
})

, childRoute Services fetchAll(), , , store, , - user , , userId .

, . , user prop .

, , :

app/services/10

10 store,

export default (store) => ({
  path: ':id',
  getComponent ({params: {id}}, cb) {
    require.ensure([], require => {
      const Manage              = require('./containers/ManageContainer').default
      const ServicesActions = require('../../modules/integrations').actions
      store.dispatch(ServicesActions.selectService(id))
      cb(null, Manage)
    })
  }
})

selectService - , state.services

, Services , , store.dispatch , Services ?

?

+4
2

TL; DR. , . HoC, .

, -, /, . : " ? , ..". , Relay, . Relay , , , "". .

, componentDidMount , "".

, , :

  • /services/
  • ServicesContainer
  • /services/10, , .
  • , , , .

, , , , , . - :

class Services extends React.Component {

    componentDidMount() {
        if (!this.props.areServicesFetched) {
            this.props.fetchServices()
        }
    }

    render() {
        return this.props.areServicesFetched ? (
            <ul>
                {this.props.services.map(service => <Service key={service.id} {...service}/>)}
            </ul>
        ) : <p>{'Loading...'}</p>
    }

}

const ServicesContainer = connect(
    (state) => ({
        areServicesFetched: areServicesFetched(state)  // it a selector, not shown in this example
        services: getServices(state)  // it also a selector returning the services array or an empty array
    }),
    (dispatch) => ({
        fetchServices() {
            dispatch(fetchServices())  // let say fetchServices is the async action that fetch services
        }
    })
)(Services)

const Service = ({ id, name }) => (
    <li>{name}</li>
)

. , . , .

- " , ?" . , ? :

, , . , .

, , , ( , , " ?" ..). , :

const Services = ({ services }) => (
    <ul>
        {services.map(service => <Service key={service.id} {...service}/>)}
    </ul>
)

Services.propTypes = {
    services: React.PropTypes.arrayOf(React.PropTypes.shape({
        id: React.PropTypes.string,
    }))
}


const Service = ({ id, name }) => (
    <li>{name}</li>
)

Service.propTypes = {
    id: React.PropTypes.string,
    name: React.PropTypes.string
}

, , , . . " , , - ". HoC.

, HoC , , . HoC - , , .

, , , . recompose - , HoC.

, ensureServices, :

  • connect
  • services
  • , services
  • services

:

const ensureServices = (PureComponent, LoadingComponent) => {

    /* below code is taken from recompose doc https://github.com/acdlite/recompose/blob/master/docs/API.md#rendercomponent */
    const identity = t => t

    // `hasLoaded()` is a function that returns whether or not the the component
    // has all the props it needs
    const spinnerWhileLoading = hasLoaded =>
      branch(
        hasLoaded,
        identity, // Component => Component
        renderComponent(LoadingComponent) // <LoadingComponent> is a React component
      )

    /* end code taken from recompose doc */

    return connect(
        (state) => ({
            areAllServicesFetched: areAllServicesFetched(state),  // some selector...
            services: getServices(state)  //some selector
        }),
        (dispatch) => ({
            fetchServices: dispatch(fetchServices())
        })
    )(compose(
        lifecycle({
            componentDidMount() {
                if (!this.props.areAllServicesFetched) {
                    this.props.fetchServices()
                }
            }
        }),
        spinnerWhileLoading(props => props.areAllServicesFetched),
        mapProps(props => ({ services: props.services }))
    )(PureComponent))
}

, services , :

const Loading = () => <p>Loading...</p>

const ServicesContainer = ensureServices(Services, Loading)

<Services> , , , <ServicesForm>, services , - :

const ServicesFormContainer = ensureServices(ServicesForm, Loading)

, react-redux-pledge, , , .

+12

, . , React Router - , onEnter/onChange.

API : https://github.com/reactjs/react-router/blob/master/docs/API.md#onenternextstate-replace-callback

, async getComponent, hook onEnter ( , getComponent), , .

- , redux-thunk:

export default (store) => ({
  path: ':id',
  getComponent ({params: {id}}, cb) {
    require.ensure([], require => {
      const Manage              = require('./containers/ManageContainer').default
      const ServicesActions = require('../../modules/integrations').actions
      cb(null, Manage)
    })
  },
  onEnter: (nextState, replace, cb) => {
      const actions      = require('./modules/services').actions
      const reducer      = require('./modules/services').default
       //fetch async data
      store.dispatch(actions.fetchAll()).then(() => {
          //after you've got the data, fire selectService method (assuming it is synchronous)
          const ServicesActions = require('../../modules/integrations').actions
          store.dispatch(ServicesActions.selectService(id))
          cb()//this tells react-router we've loaded all data  
      })
  }
})

, , , , . , .

componentDidMount .

+2

All Articles