Last week I published a story about store creation in @WalmartLabs’ Electrode. As I said, apart from that first engineering question, I had another one, this time regarding Server Side Rendering and store initialization when data prefetching is needed.
Let’s suppose we have the following case: Our application initializes a certain page by making an XHR to our server, retrieving the data to show (true SPA style). In order to Server Side Render this page, though, we need to have that data available beforehand. The real question behind all this is the following:
How can we initialize our store with such data, while enforcing code reuse as much as we can, and then feed those components+data to be Server Side Rendered? Keep in mind, that while this issue can be solved without much fuss when designing an application from scratch, I couldn’t find a clean way to do it in an Electrode app.
As a first step, I chose to build on the solution I presented in my previous story, and thus sought out to find a way to efficiently dispatch actions that perform fetch requests on our server in SSR context.
Note: If you try to dispatch actions that
fetchin SSR context, your server will either complain that the provided URL is not absolute and thus cannot move on with the request or, if you have supplied the external url of your server, will simply execute the classic XHR in order to get those data. Isn’t it a waste of resources to do so?
Let’s take a step back and take a look at our last example of the previous story:
routes.jsx we have defined our store initializers. The route
/some-route dispatches the action
getData in order to populate the state.
What if our
getData function makes an HTTP request on our own server?
It doesn’t make any sense to issue regular HTTP Requests for these, when preloading for Server Side Rendering, since they are going to be served by the same process! Furthermore, it would only make code more fragile, because we would have to specify public urls etc.
If only we had a
fetch that would operate as usual on a browser, but instead would simulate the API call when in the context of Server Side Rendering.
But why though, expose such logic to a
fetch library that gets downloaded by the client, when the context of a client won’t ever need to know the particularities of SSR?
Maybe it is better to have a Middleware that will feed our actions with the correct fetch, depending on the context this middleware is running!
In detail, this middleware shall be agnostic of which fetch library it feeds the actions. It should just pass on a specific param that was given to it upon creation. So when the store is created server side for SSR, the
inject-fetch is used. But when it is created in the context of a browser, the regular
fetch shall be used!
Oh wait, so we just want a middleware that passes on an object we give it, as an extra param to the thunk actions! But we already have one such middleware!
And in detail, from the docs:
Since 2.1.0, Redux Thunk supports injecting a custom argument using the
So we can create our
inject-fetch like this:
Add it to our redux store lifecycle like this:
initialize it with our HAPI.js server’s instance:
and use it in our actions like this:
Now everything’s in place and SSR runs smoothly!
The only downside I see at the moment, is that the actions need to be structured in a “specific” way, accessing the
fetch function from the arguments of the action.
However, please note that this implementation is simply a proof-of-concept, and there is still work to be done, specifically in the
inject-fetch to fallback to the regular
fetch when urls that don’t belong to our server are provided!
Overall, I found the proposed solution to be relatively clean and have found working with it really seamless! I would like, however, your feedback; any problems that you may see in this approach or perhaps suggestions to better it! Also, if you are aware of any other way to tackle the SSR pre-fetching issue in Electrode, please do comment and let me know!
I have created two Github repos with the respective code, one for the inject-fetch library (which I plan to publish on npm) and one with an example app utilizing the methods described in this and the previous story. Feel free to check them out, report issues or fork!