Reputation: 6013
I'm trying to understand how to connect redux-saga to NextJS and am following the sample code they provide -- https://github.com/zeit/next.js. I understand that one can load data from within getInitialProps
but I don't understand what is going on with the call to Component.getInitialProps
:
class MyApp extends App {
static async getInitialProps({Component, ctx}) {
let pageProps = {}
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps({ctx})
}
return {pageProps}
}
render() {
const {Component, pageProps, store} = this.props
return (
<Container>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</Container>
)
}
}
export default withRedux(createStore)(withReduxSaga({async: true})(MyApp))
Is this allowing all async loads within the getIntialProps of pages to load? That is, in index.js
we have the code
class Index extends React.Component {
static async getInitialProps (props) {
const { store, isServer } = props.ctx
store.dispatch(tickClock(isServer))
if (!store.getState().placeholderData) {
store.dispatch(loadData())
}
return { isServer }
}
componentDidMount () {
this.props.dispatch(startClock())
}
render () {
return <Page title='Index Page' linkTo='/other' NavigateTo='Other Page' />
}
}
Will this getInitialProps
wait to return until all the data is loaded? How will it know when it's loaded?
Any help much appreciated!
Upvotes: 0
Views: 1250
Reputation: 462
Since _app.js is a HoC your Component inside the _app.js is basically the page you want to load from the pages folder (if you compose further you can propagate another HoC and afterwards load your page, depends on your application, but then in your compose HoC you have to implement getInitialProps again and then execute the getInitialProps of your final page.js). Each page can have an own getInitialProps (eg. on your contact page you want to load your companies address and only there in your entire application). _app.js executes Component.getInitProps (if set). If the Component's method returns a non-empty object, it becomes your pageProps which you finally provide to the Component inside the render method.
Your page.js now has a getInitProps method implemented, which dispatches a saga task and returns isServer ... that given, only your store is hydrated by dispatching the action - the getInitProps does not have to wait for anything and returns a boolean. After the store hydration your component will be updated, since your props in store are loaded/updated.
However I face the same issue at the moment:
I dispatch saga Tasks in my getInitProps, but since the actions are async I receive the props after ComponentDidMount.
EDIT:
TL:DR;
UPDATE:
It doesnt work. You can only stop the sagas on the server because you only need them for the initial execution, the sagas on the server are pretty useless afterwards. However on the client side you cannot stop the sagas.
Here is my store.js... you can execute initial sagas and the server will wait for them to finish in getInitialProps, since the toPromise() makes the stopping async.
On the client side you have to work your way through lifecycles etc... client side store hydration does not work as I expected it to work. Maybe it is better since otherwise you would block React from rendering, which goes against React in general.
store.runSagas = () => {
if (!store.currentSagas) {
store.currentSagas = sagaMiddleware.run(rootSaga);
}
};
store.stopSagas = async () => {
if (store.currentSagas) {
store.dispatch(END);
await store.currentSagas.toPromise();
}
};
store.execTasks = isServer => async (tasks = []) => {
tasks.forEach(store.dispatch);
if (isServer) {
await store.stopSagas();
}
};
Upvotes: 2