Next.js — Setting up a Redux store with combineReducers()

Redux is one of the most common libraries for state management in React projects. Here is a short guide about setting up a Redux store with combineReducers() in your Next.js project. This is a guide for beginners. If you have been playing with Next.js for a while, you probably don’t need this guide. Yet, I don’t find many people talking about this from Google. Hence, I would like to provide some sample codes. In case you just start to use Next.js, I hope this can be a starter for you to quickly begin with and save you some hours from studying it.

Minimal Setup Example

To begin with, suppose you have already setup your basic Next.js project with _app root component, run npm install redux react-redux next-redux-wrapper to install Redux. Next, create a store folder inside your src folder or your root folder. After that, create some reducers to combine:

store/reducerA.js:

const initialState = {
counter: 0
}
export default (state = initialState, action) => {
switch (action.type) {
case 'reducerA/setCounter':
return {
...state,
counter: action.payload
}
default:
return state
}
}

store/reducerB.js:

const initialState = {
justAList: []
}
export default (state = initialState, action) => {
switch (action.type) {
case 'reducerB/addToList':
return {
...state,
justAList: [ ...state.justAList, action.payload ]
}
default:
return state
}
}

store/index.js:

import { combineReducers, createStore } from "redux";
import reducerA from './reducerA';
import reducerB from './reducerB';
export default (preloadState, options) => {
return createStore(
combineReducers({
reducerA,
reducerB
}),
preloadState
)
}

Finally, in your pages/_app.js:

import App from 'next/app';
import { Provider } from "react-redux";
import withRedux from "next-redux-wrapper";
import makeStore from '../store';
class MyApp extends App {
static async getInitialProps({Component, ctx}) {
//Preload from the server side
ctx.store.dispatch({
type: 'reducerA/setCounter',
payload: 10
});
return {
pageProps: {
...(Component.getInitialProps ? await
Component.getInitialProps(ctx) : {})
}
}
}
render() {
const { Component, pageProps, store } = this.props;
return (
<Provider store={store}>
<div>
<Component {...pageProps} />
</div>
</Provider>
);
}
}
export default withRedux(makeStore)(MyApp);

Logic Behind

If you are familiar with the normal React + Redux stack without Next.js, you will find the major difference here is inside the store/index.js. Instead of directly creating a store with createStore(), you are now exporting a function (makeStore()) for the Next.js instance to control the creation of store. This is to facilitate the preloading of state from the server side. This is what happen when someone visits your server:

  1. Your Next server creates a empty store with makeStore() and passes it to the getInitialProps() function.
  2. Dummy actions are dispatched to each of your reducers to initialize the state by the default switch case.
  3. You can dispatch something inside the getInitialProps() to preload the state from the server side. (or simply do nothing)
  4. Your Next server takes the currently store state after running getInitialProps() and call the makeStore() again to create a new store for components to do SSR.
  5. After SSR, the preload state will be delivered to the client side together with rendered pages. The client will start the React instance normally and also use the makeStore() to create a store with the preload state.

That’s it. Thanks for reading! Any comments would be highly appreciated. :D

Web developer from Hong Kong. Most interested in Angular and Vue. Currently working on a Nuxt.js + NestJS project.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store