Reputation: 483
I have a react app which is using react-router. I`m using plain routes, but this is how components represent my routing
<Routes>
<Route component={CoreLayout}>
<Route component={AppLayout}
onEnter={fetchData}>
<Route path='dashboard'
onEnter={fetchStatistics}
component={Dashboard}>
</Route>
</Route>
</Routes>
The situation now
First, the app layout is going to block every render while it is fetching the necessary data from the server (like the User data). Then if we have the data, we can step on the child routes, like in this case the Dashboard route, where we are loading the content of the pages.
The goal
The problem is whit this strategy, we are going to show a blank white page until the onEnter on the main route is resolved.
To avoid this, I would like to load the AppLayout component, but without starting the onEnter function on the child route. To do this, I can show a waiting spinner where the child component would load, and when the data is loaded I can start loading the child`s data.
tl;dr
The question is, how can I make the parent layout to render, while the child route`s onEnter is not loaded.
Upvotes: 1
Views: 620
Reputation: 36787
Instead of using onEnter
, you can have your <Dashboard>
initiate its data fetching in its component(Will|Did)Mount
method. Have it maintain a state.loaded
boolean which displays a spinner when state.loaded = false
.
class Dashboard extends React.Component {
constructor(props) {
super(props)
this.state = {
loaded: false
}
}
componentWillMount() {
// mock data fetch call that uses a promise
fetchStatistics()
.then(resp => resp.json())
.then(data => {
this.setState({
loaded: true,
data
})
})
}
render() {
// if data hasn't been loaded, render a spinner
if (!this.state.loaded) {
return <Spinner />
}
// data has been loaded, render the dashboard
return (
<div>...</div>
)
}
}
It doesn't handle data loading errors, but here is an example of a general purpose data loading HOC that should work (haven't tested it):
/*
* @Component is the component to render
* @fetchData is a function which fetches the data. It takes
* a callback function to trigger once data fetching has
* completed.
*/
const withData = (Component, fetchData) => {
return class WithData extends React.Component {
constructor(props) {
super(props)
this.state = {
loaded: false
}
}
componentWillMount() {
this.props.fetchData(() => {
this.setState({ loaded: true })
})
}
render() {
return this.state.loaded ? (
<Component {...this.props} />
) : (
<Spinner />
)
}
}
}
Usage
function fetchStatistics(callback) {
fetch('/api/dashboard')
.then(resp => resp.json())
.then(data => {
dispatch(dashboardData(data))
callback()
})
})
<Route
path='dashboard'
component={withData(Dashboard, fetchStatistics} />
Upvotes: 1