Server Side Rendering in React/Redux (JS)
- The purpose of server side rendering is to get data in the screen as soon as possible. In order to engage the user and give him a sense of immediate response.
- In this application we have a separation between api server and rendering server. It is a common architecture to be able to scale the servers, in case we get huge amount of calls to the api the get data. Or because server side renderer is really slow, we may need to scale the renderer servers to keep up with the traffic and speed that we want the application to be displayed on the screen.
1 we create an express server to create the base for our React/Redux app. One required library is going to be “react-dom/server”, in particular the “rendertoString” function. To create raw html that will be rendered once and sent to the client side.
With the webpack approach, where we will define the babel that will allow us to write code with the latest js style, even if it is not supported yet by the browser. And create a bundle file with all the js code related with our app.
In the webpack configuration we will define the entry point of the app. The target to specify if we have client or server (node) running the app. Outputs defining name and folder where we will build our app. And rules like what files is babel going to run and additional options to define react.
In this chapter, we solved problems related with running JSX code in the server through webpack that generated a bundle file transpiling JSX to vanilla javascript. And solved the problem to run components in the server using the “react-dom/server ”and “renderToString” method. That create
2we need to restart the server every time something changes in the components, using a nodemon library.
We will create an alias in the script in the “package.json”. Running on the terminal webpack and the server.
In case the terms “Universal Js” or “Isomorphic Js” cames up, that are essentially other fancy names for Server Side Rendering.
At the of this step we get html being sent from the server to the client, but no code is being sent related with listener like button clicks and so on…
3we create a new webpack config file for the client to solve the previous problem. The entry needs to point to the src folder to a client file. Nith a new path as well so that the new bundle is created in a separate place.
app.use(express.static('public'));
<html>
<head></head>
<body>
<div id="root">${content}</div>
<script src="bundle.js"></script>
</body>
</html>
This code added to the server allows the newly created bundle file from the webpack for the client to be available to the app. We the use of a snippet of html code that will be added to a script tag associate with that snippet. Because of the static reference there is no need to specify the path like “/public/bundle.js”.
This logic is a solution to the problem were the server bundle file sent to the browser is like the skeleton on our page. While the bundle created by the client bundle are the events handlers and everything that makes our App nice and responsive!!
This is the hydration which is the process of putting functionality back the DOM that was already rendered. Basically replace the render method call by the hydrate() and we avoid an warning in the console saying that from react v17 forward that is going to be the new call.
module.exports = merge(baseConfig, config);
In order to use the DRY concept (Don’t Repeat Yourself), we can create a third webpack file were we will write the code that is the same for both Server and Client webpack file. Using the merge function.
4with the library npm-run-all we can join the multiple webpacks and server we need to run using the equivalent of threads into a single terminal. In the terminal we run:
npm-run-all --parallel dev:*
That way we save a lot of typing when we need to run our app.
externals: [webpackNodeExternals()]
To prevent webpack to import our libraries into the server module to have a more clean and quick start we will use the module webpackNodeExternals. Because we are not shipping anywhere it is not a big deal. But still it will run quicker every time we need to run the server webpack.
5in this chapter we are approaching the routing of our app (client and server).
app.get('*', (req, res) => {}
In this project we are using a 2 tier architecture with an express router and a react router. The express router will receive any url with the wildcard “*”. It will delegate what to do with that url to the react router.
A problem to solve is that the BrowserRouter reads the url in the browser, which it does not exist when the applications is hydrated (rendered) in the server. It doesn’t work in the server side. A solution is to use a StaticRouter in the server and a BrowserRouter in the client.
- Server:
<StaticRouter location={req.path} context={{}}>
<div>{renderRoutes(Routes)}</div>
</StaticRouter>
);
- Client:
<BrowserRouter>
<div>{renderRoutes(Routes)}</div>
</BrowserRouter>,
document.querySelector('#root')
);
6in this chapter the Redux is integrated into the project. From the api call using axios and thunk. Again, duplicate the creation of a store in a similar fashion as it was done previously with router. Plugin the code necessary for the actions creator and reducer. Finishing with the new UI component (HOC: high order component) for the users.
Still, there is no data loading on the server side yet.
7in this chapter we are defining the routes using a library called react-router-config. It is necessary to map the routes as an object instead of the Route to avoid rendering the hole app. Just the components we need to a specific url.
Finally we need to create a loadData function in the component that is going to be rendered when the url is called in the router. Where we will dispatch a manual action creator in our case to fetch users.
This is the moment where we can disable javascript in the web page and see the magic of rendering a page on the server happening.
At last feel free to run the hole app with the full boilerplate working using:
git clone git@github.com:atafs/fed-server-side-rendering-with-react-and-redux.git
Although this article is just explaining until the commit:
git clone 4c8f1bbc5210ac79d1b58a5ba54a06ec731f39aa