State Management & Hydration with MobX — We must React [Ep. 05]
Before reading this article, I suggest you to take a look on the previous one, which explains the concepts and advantages of Isomorphic apps and how to make a basic server side rendering implementation with React Router and a minimal Express server:
In this episode we will see how to do State Management in React using MobX and implement an isomorphic way to fetch components data for Server Side Rendering with State Hydration.
Index
- Introduction to MobX
- State Hydration
- Initial State Injection
- Creating the Stores
- Updating the Isomorphic Middleware
- Isomorphic Data Fetch
- State Rehydration and Store Injection with Provider
- Inject Store or FetchData
Introduction to MobX
1 For the implementation of the state management I picked MobX instead the most popular Redux.
I think MobX is really cool and powerful, you’ll love it!
MobX is a battle tested library that makes state management simple and scalable by transparently applying functional reactive programming (TFRP).
The philosophy behind MobX is very simple:
Everything that can be derived from the application state, should be derived. Automatically.
In other words, MobX makes your stores observable and automatically reflects the state on the components when any observed data changes.
For a quick start, take a look at the Core Api on the official MobX Documentation.
State Hydration
2 The state hydration is a process needed to pass data from the server to the client; It’s divided into 2 parts:
Dehydrate (on server):
- get the initialized stores;
- inject the stores into the html;
Rehydrate (on client):
- get the initial state from the html;
- inject the initial state into the stores;
src/state/hydrate.js
initStore is a function that handles the Initial State Injection.
Initial State Injection
3 Now we can instantiate the stores and create a single object which will feed the mobx-react Provider.
src/stores.js
Creating the Stores
4 First of all,
we need to create the needed stores in src/stores:
src/stores/app.js
The constructor gets the initial state and assigns it to the object.
ssrLocation will store the server location (from the isomorphic middleware) that we want to pass to the client to performs some checks afterward, when we load the data from the api-server. We will deepen this later in this article.
You’re free to create the remaining stores according to your needs. Just keep in mind to use the MobX @observable decorator on class properties to make them reactive.
Updating the Isomorphic Middleware
5 Now we need to update the iso-middleware. The handleRouter() will call the fetchData(store, components, params, query) which maps the containers searching for a static fetchData({ store, params, query }) method in them to call;
After all the data is fetched, the store is ready to be passed to the mobx-react Provider and than the app is rendered with the dehydrated state.
src/server/middleware/iso.js
The static fetchData() defined in the containers will implement the call at the REST or Socket APIs.
It’s very important that static fetchData() returns a Promise.
Isomorphic Data Fetch
6 The fetchData() is called on the iso-middleware, but we use this function to fetch data also on the client-side when the router location changes. This is achieved using fetchDataOnLocationMatch() which uses ssrLocation (passed from the server-side to the client-side) to avoid that the data loaded from the server is re-loaded on the client.
src/utils/fetch.js
fetchDataOnLocationMatch() will be called on the client.jsx entry point.
State Rehydration and Store Injection with Provider
7 In the client entry point we need to rehydrate the state (which will inject the Initial State into the Stores) and then inject the Stores into the mobx-react Provider.
src/client.jsx
Inject Store or FetchData
8 From now, within all Container components we can access the stores injecting them using the mobx-react @observer([]) decorator or using the static fetchData() like this:
We are implementing static fetchData() calling a particular store action method (and returning the Promise) only when we want to update the state for that specific component. It will be executed only when the router matches that component.
If you want to see a working implementation of these concepts, I created a repo which I called RFX-Stack (no longer supported, see RFX-Next) based on these three main technologies: React, Feathers, MobX.
It has more advanced functionalities, such as Functional & Modular CSS and auto-injection and initialization of state into stores, so you only have to do is register a class into the store.js and write store classes with no need to deal with manual state injection.
Both RFX-Next and RFX-Stack uses the RFX-Core package for handling all the state management described in this article.
That’s all. I suggest also to check out Feathers because it’s a very cool framework.
Feathers is a minimalist, service-oriented, real-time web framework for modern applications that puts real-time communication at the forefront rather than as an afterthought. http://feathersjs.com/