quintessential-loading
guy with facial dark hair wearing a hoodie looking on his left. Black and white background
2021-03-05 :: 9 min read
John Chantzigoulas
Share on

How to integrate Redux — with Next.js and SSR

Overture

Probably most of you have at least once, set up redux, with or without the use of Redux Toolkit, for a project of yours, so this will not be your typical “how-to” article about redux and react — although some typical introduction will be made. In this article, we are going to discuss how we can setup redux, and particularly Redux Toolkit, so that the application state will be accessible from both the client-side as well as the server-side of the Next.js project— by the client and server-side we don’t refer to the frontend and the backend of the application, but instead we use the term client-side to describe the part of our code that runs of the Browser (client) and the term server to describe the part of our code that runs on the next.js server when our page is being rendered due to Server-side rendering (more on SSR later on). So, let us talk a bit more about Next.js and its properties in regards to SSR, Redux Toolkit, and how to combine all of the above.

grey background  with the logo of next.js and redux

Let’s begin…

A short introduction to Redux — Redux Toolkit

Many of you are probably familiar with the combination of React (or create-react-app) with Redux. Redux is an excellent tool to use in order to handle the state management of your application, although it comes with one major drawback, TOO damn much boilerplate code. If you had any experience with integrating Redux with a react app that has a considerable size, you will know that you end up having to implement the same pieces of code, again and again, and again, just to get ready to do some actual work. That after time causes unnecessary fatigue and takes a large part of your time from the real coding (or solving bugs…). To combat this the creators of Redux have come up with Redux Toolkit.

“Redux Toolkit the official, opinionated, batteries-included toolset for efficient Redux development. Is intended to be the standard way to write Redux logic.”

It was originally created to help address three common concerns about Redux:

  • “Configuring a Redux store is too complicated”
  • “I have to add a lot of packages to get Redux to do anything useful”
  • “Redux requires too much boilerplate code”

With the above comments stated we can understand why using the redux toolkit is quite a tool to have, although I always suggest to anyone that is just starting out with redux, to start using it without the help of the toolkit so that you can:

  • First and foremost, understand how Redux and its mechanisms actually work.
  • Secondly to appreciate the help that the Redux Toolkit provides down the road.

Coming up — Next.js

Next.js is the next big (🤡) thing for React applications. It is one of the most complete Frameworks for creating React applications, providing best developer experience with all the features you need for production: hybrid static & server rendering, TypeScript support, smart bundling, route pre-fetching, and more. No config needed.

Next.js and SSR — Server-Side Rendering

Next.js provides out-of-the-box support for server-side rendering of your page without any extra configuration from your part. There are two methods of Pre-Rendering a page when using Next.js.

Methods of pre-rendering with next.js

  • Static Generation (SSG): when this method is used your page is generated completely on build time and is reused on each request.
  • Server-side rendering (SSR): The HTML is generated on each request. This means that each time a request is made to the server, then your requested page is rendered and send back to you.

In regards to the above, the key difference between Static Generation (SSG) and Server-side rendering (SSR) is when pages are being built-rendered.

red-orange background with white letters and the logo of next js

Data Fetching with Next.js

As we have seen above, Next.js has two methods of pre-rendering pages, but how you decide which method is right for you?

The main thing that can help you decide how you want to pre-render your page with Next.js is whether you can pre-render your page ahead of the user’s request or not, depending on what data your page is showing. If your page shows data that are frequently updated, then you must use the SSR method, on the other hand, if your page doesn’t have requirements for frequently updated data then your best option is to go with SSG, as this method has the better performance between the two.

“Let’s dive into more details on how we can fetch data when using SSR.”

Fetch Data on the server-side with ‘getServerSideProps’

In order to fetch data on the server-side when using Next.js, you must export an ‘async’ function called ‘getServerSideProps’ from your page. In this way, Next.js will pre-render your page with the data returned by ‘‘getServerSideProps’’.

So, let’s say that you want to fetch some data from an external API on the server-side so that your page is already populated with some data before it reaches the client-side. You can request those data inside the “‘getServerSideProps’’ as described below:

coding lines on a black background

Now, let’s consider what will happen if we want to fetch some data from an external API or even handle a cookie inside a request, for example, to retrieve the authentication token and store them directly into redux, but remember we are still on the server-side, is it possible to get the state that we have initialize on the server-side and “HYDRATE” the state of the client-side?

Read on to find out…

Combining Next.js with Redux Toolkit and Preserve State during SSR

First of all, we need to create our redux store using the “createStore” function provided by the Redux Toolkit.

const createStore = (preloadedState) => {
return configureStore({
reducer: {
nextRepo: nextSlice,
},
preloadedState,
});
};

As you can see above, we pass the preloadedState value to the configureStore. This value is used to HYDRATE the state with the server fetched data — or any other pre-existing data for that matter.

The second step is to correctly initialize our store depending on which side of the application we are currently operating. As you can see below on the code snippet, we perform a check to see whether our code is executing on the client-side or on the server, by checking the value of the window.

let store;
export const initialiseStore = (preloadedState) => {
let _store = store ?? createStore(preloadedState);

if (preloadedState && store) {
_store = createStore({ ...store.getState(), ...preloadedState });
store = undefined;
}

// For SSG and SSR always create a new store
if (typeof window === 'undefined') return _store;
// Create the store once in the client
if (!store) store = _store;

return _store;
};

You can see that if a preloadedState is provided to the initialiseStore method and a store has been already created the new store that is created will include both the old state and the preloaded state that in most cases will be the props they were fetching on the server-side.

Finally, we create a memoized method with which we can safely initialize the store once on the client-side, without losing the old existing state.

It’s time to put into action what we created above

— It’s time to examine one of the custom files of Next.js, ‘_app’

Next.js uses the ‘_app’ file or better ‘App’ component to initialize pages. This means that one place that you would probably want to use the redux on the server-side to initialize some data is in this component.

Before we dive into the code there is a major detail that we must point out. Previously, we have mentioned the getServerSideProps and Data Fetching methods. Those methods aren’t yet supported by ‘App’ but instead, we have an alternative ‘getInitialProps’.

‘getInitialProps’ is to be used in similar ways to ‘getServerSideProps’, it enabled server-side rendering on a page and allows the initial data population

Enough talking, let’s see the code 🔥

In a similar fashion to ‘getServerSideProps’, ‘getInitialProps’ runs on the server, which means, that we can fetch data from an external API and populate our state before reaching the client-side.

Our 1st step is to initialise the redux store, without supplying a value for the preloaded state (you could do that if you’d like 👍), after we have initialised our store, the returned value allows us to extract the “dispatch” method of the redux store:

MyApp.getInitialProps = async (appContext: AppContext) => {
const appProps = await App.getInitialProps(appContext);
//initialise redux store on server side
const reduxStore = initialiseStore({});
const { dispatch } = reduxStore;
const res = await fetch('https://api.github.com/repos/vercel/next.js')
const json = await res.json()
dispatch(setStars({ stars: json.stars }));

appProps.pageProps = {
...appProps.pageProps,
initialReduxState: reduxStore.getState(),
};

return appProps;
};

We use the API from the example in the Next.js documentation, to fetch some data from an external API, and then we dispatch the “setStars” action that will add the value of the stars to our store.

At this point, our store has been initialized and the state is already holding some data. You can see that we are including the reduxStore.getState() data to the returning value of ‘getInitialProps’, in the field “initialReduxState”.

This field can now be used by the ‘App’ component to get the initial value of the store:

const MyApp = ({ lang, Component, pageProps }: myAppProps) => {
//Normal initialisation Redux on client side
const reduxStore = useStore(pageProps.initialReduxState);
return (
<>
<Head>
<title>Next.js - Redux</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<Provider store={reduxStore}>
<Component {...pageProps} />
</Provider>
</>
);

As you see, we use the memoized method that we have implemented above, “useStore” in order to initialize our store, this time on the client-side, but we are able to retain the state that we have populated on the server-side.

The rest is done, as usual, we provide our store through Provider component, and the store is made available to all our components — with the data that were fetched on the server-side still available 💪 .

matrix neo, dark skin bald guy with dark glasses

References

quintessential-loading