What's the cleanest way to populate a component at load time? Lets say I have a fractal section with a main container and an action that fetches all the data for a page (fetchAll).
import React, { Component } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import actions from '../modules'
import MainComponent from '../components/MainComponent'
class MainContainer extends Component {
//
// ??? BEST PLACE TO FETCH ALL ??
//
componentWillMount() {
this.props.actions.fetchAll()
}
render() {
return (
<MainComponent {...this.props} />
)
}
}
const mapStateToProps = (state) => ({
users: state.users
})
const mapDispatchToProps(dispatch) => ({
actions: bindActionCreators(actions, dispatch)
})
export default connect(mapStateToProps, mapDispatchToProps)(MainContainer)
Is the best place to put a method that loads all the content for a page at componentWillMount? Will this cause any type of double rendering / performance hit?
avoid using component lifecycle methods in general, especially for data loading
a simple solution would be dispatching an action to fetch data in the route definition's getComponent callback, which is somewhat unique to this structure. the router will pass nextState for accessing things like route params. avoid blocking and set appropriate state so your component can render a spinner etc.
you should not need jsx at all in the container component
Excellent. Would the following be "kosher"?
import { injectReducer } from '../../store/reducers'
export default (store) => ({
path: 'example',
getComponent (nextState, cb) {
require.ensure([], (require) => {
const container = require('./containers/Main').default
const actions = require('./modules').actions
const reducer = require('./modules').reducers
injectReducer(store, { key: 'example', reducer })
// Call fetchAll
store.dispatch(actions.fetchAll())
cb(null, container)
})
}
})
Can you elaborate on "the router will pass nextState for accessing things like route params. avoid blocking and set appropriate state so your component can render a spinner etc." The docs for nextState are kind of lacking.
The docs for nextState are kind of lacking.
yea it was recently added in 2.2.0... nextState has same signature as RouterState:
type RouterState = {
location: Location,
routes: Array<Route>,
params: Params,
components: Array<Component>
}
Can you elaborate
surely, here's some code...
import { injectReducer } from '../../store/reducers'
export default (store) => ({
path: 'example/:id',
getComponent (nextState, cb) {
require.ensure([], (require) => {
// side note: *always* capitalize react components! it's a widely used
// heuristic for detecting components in things like babel plugins
const MainContainer = require('./containers/Main').default
const actions = require('./modules').actions
const reducer = require('./modules').reducers
// async reducer contains state management logic for this fetch,
// ie. FETCH_EXAMPLE_START => state.ui.loading = true
injectReducer(store, { key: 'example', reducer })
// use param from nextState to fetch specific example by id
store.dispatch(actions.fetch(nextState.params.exampleId))
// return component immediately with fetch in flight (non blocking)
cb(null, MainContainer)
// alternatively, you could use redux-thunk to block transition
// if you define an onError handler on Router itself, something like...
store
.dispatch(actions.fetch(nextState.params.exampleId)) // set global spinner
.then(() => cb(null, MainContainer)) // success, data loaded, render component
.catch(cb) // return error to router (or render NotFoundComponent)
})
}
})
i hope this helps!
@justingreenberg that's perfect... Thanks so much for the help.
@justingreenberg saves the day as usual.
Closing since there's no additional action to be taken in this project.
It may be worth adding this one to FAQ? :) even though its not specific to this tutorial.
I am finding a lot of answers for my questions as closed issues here. It may be worth to link some of them to "Newbies" section of FAQ. So people like me can find them easier :)
@justingreenberg
avoid using component lifecycle methods in general, especially for data loading
What about a scenario where we are trying to dynamically load a list of rooms in a side navigation pane and the data that needs to load isn't route specific?
The navigation pane is a container, I could imagine sending a dispatch on ComponentDidMount to populate the nav items would work.
I only want to load the nav items once, when the page initially loads, regardless of the current route.
What is your suggestion in that case?
@kylesavant I think you might want the data be in the top-level store, the pass the data as items props in your navigation.
Most helpful comment
yea it was recently added in 2.2.0... nextState has same signature as RouterState:
surely, here's some code...
i hope this helps!