Jibin Thomas

August 11, 2019

Next.js + Redux

Integrating redux to your server side rendered application in nextjs

web javascript

NOTE: A updated version of this post is available here.

Next.js + Redux

Integrating redux and nextjs requires some extra steps because you have to create a redux instance on every page, which is not the case in single-page applications. So, we need to create a wrapper that initializes redux store on all the pages.

There is a package called next-redux-wrapper, but for some reason, it wasn’t working for me. So, I created a wrapper instead of relying on other packages.

In case, if you don’t know about nextjs and redux,

Nextjs is a react framework used to make your websites server-rendered which increases the performance of your site and also has SEO benefits.

Redux is a popular state management library in the react world. If you have a large web application, it makes sense to separate and the application state and view.

First, let’s install the required packages, you can use npm or yarn for installation

 npm init -y
 npm install react react-dom next redux react-redux react-thunk

Optional package

npm install redux-devtools-extension

Configuring Redux in Nextjs

Next.js initializes page using the App component, and we will be overriding it to initialize redux store in _app.js. with-redux-store.js is a higher-order component that will be used in _app.js page.

This wrapper file is from next.js Github repository

//    /lib/with-redux-store.js
import React from 'react'
import { initializeStore } from '../store'

const isServer = typeof window === 'undefined'
const __NEXT_REDUX_STORE__ = '__NEXT_REDUX_STORE__'

function getOrCreateStore(initialState) {
	// Always make a new store if server, otherwise state is shared between requests
	if (isServer) {
		return initializeStore(initialState)
	}

	// Create store if unavailable on the client and set it on the window object
	if (!window[__NEXT_REDUX_STORE__]) {
		window[__NEXT_REDUX_STORE__] = initializeStore(initialState)
	}
	return window[__NEXT_REDUX_STORE__]
}

export default (App) => {
	return class AppWithRedux extends React.Component {
		static async getInitialProps(appContext) {
			// Get or Create the store with `undefined` as initialState
			// This allows you to set a custom default initialState
			const reduxStore = getOrCreateStore()

			// Provide the store to getInitialProps of pages
			appContext.ctx.reduxStore = reduxStore

			let appProps = {}
			if (typeof App.getInitialProps === 'function') {
				appProps = await App.getInitialProps(appContext)
			}

			return {
				...appProps,
				initialReduxState: reduxStore.getState(),
			}
		}

		constructor(props) {
			super(props)
			this.reduxStore = getOrCreateStore(props.initialReduxState)
		}

		render() {
			return <App {...this.props} reduxStore={this.reduxStore} />
		}
	}
}

It allows keeping the state when navigating between pages as it wraps MyApp component, which will be available on all the pages with withReduxStore higher-order function.

//   \_app.js
import App, { Container } from 'next/app'
import React from 'react'
import withReduxStore from '../lib/with-redux-store'
import { Provider } from 'react-redux'

class MyApp extends App {
	render() {
		const { Component, pageProps, reduxStore } = this.props
		return (
			<Container>
				<Provider store={reduxStore}>
					<Component {...pageProps} />
				</Provider>
			</Container>
		)
	}
}

export default withReduxStore(MyApp)

Unlike in a single-page application, we will return the initialize store function instead of the store itself.

//  /src/store.js
import { createStore, applyMiddleware } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import thunk from "redux-thunk";
import rootReducer from "../src/reducers";

export function initializeStore(initialState = {}) {
  return createStore(
    rootReducer,
    initialState,
    composeWithDevTools(applyMiddleware(thunk))
  );

Now all the components can have access to the redux store using connect function from react-redux.

Dispatching actions in getInitialProps

You can dispatch redux actions in getInitialProps via reduxStore object. You can destructure the reduxStore object from the getInitialProps parameters.

//   /pages/index.js
import React from 'react'

function index() {
	return <h1>Dispatching actions from getInitialProps</h1>
}

index.getInitialProps = ({ reduxStore }) => {
	reduxStore.dispatch({ type: 'INCREMENT' })
	return {}
}

Source code

Here’s the source code if you want to try it out in your machine.

git clone https://github.com/jibin2706/nextjs-redux.git
cd next-redux-example
npm install
npm run dev
← Back to Writing