How to use redux-thunk with Next.js?
The first time I tried to use redux-thunk
with Next.js it gave me some headache to make it work with the server-side rendering.
The problem
The getInitialProps()
method can be an async function if it returns a Promise
or uses the async/await
keywords. Without these Next.js will not wait for the async operations to finish, and will render your component before that data would be fetched and set on the store. Yikes…
Although it’s quite straightforward, it was not obvious for me from the with-redux example:
static getInitialProps ({ store, isServer }) {
store.dispatch(serverRenderClock(isServer))
store.dispatch(addCount())
return { isServer }
}
The solution
If you are using a thunk then the good thing is that the dispatch()
will return the value of the action-creator, in this case the Promise chain itself.
The important thing is that you return a Promise
from the getInitialProps()
method.
// Items model (models/items.js)
// -----------------------------import axios from 'axios';export default function reducer (state = [], action) {
switch (action.type) {
case 'SET_ITEMS':
return action.items;
default:
return state;
}
}export function fetchItems () {
return dispatch => axios.get('/api/items')
.then(({ data }) => data)
.then(items => dispatch({ type: 'SET_ITEMS', items }));
}
// Items page (pages/items.js)
// ---------------------------import React from 'react';
import withRedux from 'next-redux-wrapper';
import { fetchItems } from '../models/items';
import { initStore } from '../store';const Page = () => (
<div>
Rendering subcomponents...
</div>
);// Option 1 - Returning the Promise
Page.getInitialProps = ({ store }) => store.dispatch(fetchItems());// Option 2 - Using async / await
Page.getInitialProps = async ({ store }) => {
await store.dispatch(fetchItems());
};export default withRedux(initStore)(Page);