React Server Side Rendering with Simple Redux Store Hydration
--
So you’re building a site using React, have React Router managing what components to render given a particular URL and finally have Redux looking after the state. This appears to be the go-to architecture these days and theres nothing wrong with that. Now what if you want to render your entire application on the server and send it down to the client? There are a few examples I’m sure you’ve come across that tackle this question and do so rather effectively so I don’t plan to reiterate what they’ve already said. This post is more focused around a particular requirement of that question I’ve noticed more and more people having the desire for and something I’ve looked into extensively myself.
Server Side Store Hydration
Resolving any asynchronous requests for additional content/data on the server, as opposed to the client, can be a pretty big deal and depending on your architecture or needs it can have some huge advantages. Say you need to make an API request to fetch all the content for a particular part of your page. Well if that API is self consuming (the end point sits on the same server you’re serving the application from) you can save yourself the round trip request from the browser to the server and then back down to the browser again. We just hydrate the store with the contents of this request before we serve the application down to the client. This is true for external end-points too. Your server might just be better situated to make the request than the client is.
For SEO reasons! We may actually want to serve meaningful content rather than a barebones scaffolding of your React application. Serving a page that contains what would actually be there once your application has fully loaded (with content) without the need for the client to bootstrap all this can be a huge win for some.
Whatever the rationale is behind wanting to hydrate the store on the server how do we actually do it? Well if you’re using redux-thunks, which most people are, you’re probably doing something like this within your component:
Then on the server to dispatch and await the resolve of these we would do something like:
This is obviously just one approach and there are others (such as using asyncConnect) but they all appear to do the same thing even if it’s abstracted slightly. That’s fine and does the job however I feel there could be a better, cleaner way. Something that doesn’t involve having to change the way we write our React components and disrupt the server side code less, not to mention maintaining pure function actions.
Redux Sagas
You may have heard of redux-sagas before and if you have you’ll know they can be a little hard to get your head around to begin with, mainly due to their use of generator functions. However, this post isn’t about how sagas and generators work, theres plenty of stuff out there on that. Rather I’m going to go over how we can utilise sagas to cleanup the problem’s I’ve just mentioned above.
To understand the solution we do need to know a little about how sagas are run within the Redux ecosystem. When you create a saga you typically want it to run whenever the associated action is dispatched, and to do this our sagas are executed like a background task that listen for a particular event to be dispatched to our store. They would then ‘wake up’, execute the assigned task and then go into the background once more and wait for their action to be dispatched again.
This is how that might look:
Again, if you’re not familiar with generator functions this is going to look a little odd but basically whats happening here is we’re setting up a ‘watch’ task that will be put into the background. It will then wait for our regular Redux action to fire, in this case `PHOTOS_REQUEST` and then execute the function `requestPhotos`. Not to go into the internals of that function but it basically makes a request to an API that returns a bunch of photos and then fires another action that sends these photos onto our reducer and in turn, our store.
How do we tell our application we want this to execute? Simple, we just dispatch the request in our component like:
Looks pretty clean compared to our thunk example above right? So how do we make this request on the server also? Firstly we need to somehow get access to our sagas promise object to ensure we render the application only once all our executing saga tasks have completed. This promise is actually exposed when we initially start running our sagas:
You’ll see here we’re hooking into the `done` key that is returned. However it isn’t quite as simple as this and to understand why we have to take into account the fact our sagas are run like background tasks. What I mean by that is the sagas you create will, for the most part, run against all occurrences of a particular action being dispatched. To do this the generators we create utilises a `while` loop (the example above uses the `takeLatest` helper but trust me, there’s a while loop abstracted away in there somewhere). What this means for us is the `done` promise won’t ever resolve and your server will hang waiting for a response to be served, not good!
END to the Rescue
Well a very helpful resource was proposed recently and added into redux-saga that helps us get around the background task listening problem. `END` is the what we’re interested in. Dispatching this special redux-saga action into our Redux store will essentially tell our running sagas to complete running their current task (when the generator returns `{…, done: true}`) they will then break their ‘listen — wakeup — execute’ loop and resolve. Once all running sagas have done this our runSaga promise will finally resolve.
This is how that looks:
You might have noticed the double call to `renderToString`, one where we store the returned string and the other where it is discarded completely. The reason we do this relates to how React mounts components on the server vs the client. On the client both the `componentWillMount` and the `componentDidMount` will executed, but on the server only `componentWillMount` is executed. So, as our action is dispatched in the ‘will mount’ phase of the component lifecycle we can make a call to `renderToString` and this will then fire and be picked up by our running saga task. We would then go on to `close` (END) the store so once all tasks finish iterating over their generators and are ‘done’ we finally get a resolve. We then take a snapshot of our application’s state and the DOM string when it’s been fully hydrated before rendering the entire thing down to the client.
These lifecycle methods actually give us a nice hook into what actions we want to hydrate on the server vs leaving them to the client to populate (by dispatch the action within `componentDidMount` and not `componentWillMount`).
And that’s it, a server side rendered application with simple redux store hydration. If you’d like a fleshed out example of the snippets provided in this post feel free to take a look at the entire repo: