Introduction
The motivation for this article is that I want to use redux with NextJS in my next side-project. As you know NextJS is a really powerful react framework to handle either SSG (static site generator) or SSR (server-side rendering), both of these techniques would execute some code in a server before rendering the HTML to the client.
Previously, I always use redux in a CSR (client-side rendering) web app, and now when I want to dispatch some actions inside the function in the getStaticProps
or getServerSideProps
function in NextJS, We would have to set up additional stuff to deal with it, which makes me want to see how it actually works when using redux with SSR.
The Purpose of SSR
I will simply go through some points of the advantages which SSR provides
Better SEO
SSR will return authenticate HTML template from a server when the client visits our website, unlike the modern SPA framework which will only show the tag of <div id="App"></div>
, showing the full HTML of the page will help browser crawlers to get more insight into the website and improve the SEO.
UI might appears faster
In a CSR application, the view of the web page will be visible after the javascript file is loaded, while in an SSR application, the HTML template will first appear(with no interactivity) and the javascript will start loading to give our web page interactivity. So in most cases, the web page will be visible to the user faster in an SSR application than in a CSR application.
and I think there are more advantages and caveats explained on the web
Redux on the server
As we know, the most common usage of SSR is to handle the initial render of a user requesting our web page, and here are the steps.
- The client requests our web page
- The server receives the request and tries to transform the required components into a string
- The server sends back the HTML string with a javascript file instead of a single
bundle.js
file back to the client.
The server also has to deal with the initial state we want and send it back with the HTML string from the server and here is how we handle redux on the server.
According to the Redux documentation, here’s what we need to do
- create a fresh, new Redux store instance on every request;
- optionally dispatch some actions;
- pull the state out of the store;
- and then pass the state along to the client.
And on the client side, we will initiate the store with the state passed back from the server.
Redux’s only job on the server side is to provide the initial state of our app.
Let’s walk through some code
First, we will set up an express server, whenever the client makes a request to our web page, the handleRender middleware function will be executed.
Next, here is the implementation of the handleRender
middleware function and renderFullPage
function. The handleRender middleware
function simply does three things.
- create a redux store
- fetch the initial state of the application
- call the
renderToString
method to get the HTML string returned to the client
In the renderToString
function we stored the preloaded state inside a global variable named window.__PRELOADED_STATE__
After all these steps, we are back to our client side.
On the client side, we first initialize our store using the pre-defined state in the window.__PRELOADED_STATE__
and hydrate the HTML string which the server responds to us.
That’s what it is stated in the redux official doc of how to do SSR with redux.
Preparing the initialState by request parameters
When we use libraries like react-router
we want to mutate the state differently by the URL or request parameters, how should we do it?
By parsing the req
parameter exposed by express, we can return different initial state to the client side by different routes and request parameters.
Async state fetching
When we want to mutate the state according to some asynchronous request, like fetching data from our database. here is how we do it.
Create a method that accepts a callback
We first create a method that accepts a callback function after 500 mili-seconds to simulate an HTTP-request
In the server, we execute this function and input the callback to return our state according to the result of the asynchronous request.
We could do a lot of customization here, for example, match each requested routes by a Router object to execute the API calls for each routes.
Using Redux in NextJS
In NextJS, using the library next-redux-wrapper
is a common way for you to deal with redux in the server-side
the process of using redux in server-side functions like getStaticProps
and getServerSideProps
is similar to the above steps but with a little tweak, here are the rough steps.
- Create the store on the server-side
- Dispatch the desired actions
- The server-side returns the latest piece of state
- In the client side, when you navigate to a page, a HYDRATE action is dispatched with the latest piece of state, you have to update the store according to your need.
If you want to know the details, their Github has explicit documentation.
Conclusion
To conclude, similar to the client side, when we want to dispatch an action in our server, we need to have all the required elements (store, reducers) in order for us to produce the correct state and return back to the client side.