A State management comparison with React hooks, mobx and recoiljs

David Bottiau
4 min readJun 1, 2020

--

With the evolution of the web ecosystem and React in particular we found several way to manage the state of our applications. It gives us a really simple and evolutive architecture on the frontend, the f(state) = UI lifecyle. Let’s see in details where we are going by focusing on the state management current trends: hooks, mobx and recoiljs.

History

However, if we focus on the pure React ecosystem, there is some kind of a tradeoff regarding the different state management solutions whether it is in performance or in simplicity.

The first solution was the setState pure function. It works great for a small app but it can become really hard to maintain at a certain point where you want to pass down props from parent to children.. and grand-children.. and grand-grand-children.. Well, that’s where Redux comes into place.

Redux on the other hand completely separate the state and the UI components. That becomes handy because you can pick whatever data you want anywhere you want. This is a genius move because you can decouple your architecture between a state, actions, reducers, selectors and components. But of course, the tradeoff is that it requires a lot of code to achieve this.

The new way

So, there are new ways to manage your state and with different objectives:

Pure React hooks — equivalent to the setState pure function but it gives you

  • the ability to create only functional components (less code)
  • you can also split each part of the state into different hooks
  • if needed, you can share your states using the Context api
  • only consume and updates from the top level

Mobx — smart observations based on a mutable state

  • specify — not so functional — Classes that contains updatable data
  • ultra performant: only the components which consumes the properties updated gets updated
  • requires a good knowledge of the API: @observable, @action

Recoil — a brand new state management library based on decentralized states (called atoms)

  • works well with React hooks and indeed it mimics the useState API with new names: useRecoilState, etc…
  • you can reuse states/atoms with selectors (almost a la Redux)
  • consume and updates on any levels

In order to understand the differences in terms of performance and even in terms of simplicity and scalability of the architecture, I made a small todo list app to compare React hooks, mobx and recoiljs. Here is the result:

It is a really small app so I imagine there is a lot more to grab to really the differences between the 3 ways of writing a React app.

If you want to see the result in performance, I strongly suggest you to install the React DevTools extension:

With this extension, you can highlight renders in the entire app:

Highlight render updates using React DevTools profiler

Conclusion

So, by writing the examples what I’ve found there is that:

  • Pure React hooks are great to start but it is one of the worst choice in term of performance
  • Mobx is really opinionated and you should follow but in term of best performance, it is the way to go
  • At the moment, Recoil seems like a good compromise between simplicity and performance. It is a fine addition to React hooks and it is neither the worst nor the best in term of performance

Indeed, if you check my code and highlights renders:

Pure React hooks

Everytime an update is made to the hook (state), all of the tree is re-render.

I managed to make a StoreContext to split code between components and hooks (data & mutations). It seems like the smartest move to reduce size of components because it matters.

On the other hand, due to the nature of hooks and rendering, it seems like you will need to split hooks into many.. many Context. It can then be challenging to maintain the architecture with so many Context.

Also note that where you place your ContextProvider can have a major impact in your performance. The higher it is in the React tree, the more re-renders you will have.

Mobx

Well, again. It is perfectly done with classes. The two main points are:

  • all the logic resides in the classes: observable data and mutations, so we still have the seperation between small components and logic
  • I can create a single StoreContext as a single source of truth. Because I don’t care of the container, the component are only render based on the consumption

Recoil

This time, Context seems useless. You have decentralized states so it feels natural to follow with decentralized logic, decentralized hooks.

Let’s just start with atoms:

We can now create specific hooks for data and mutations:

And voila. You are ready to use your new hooks.

Your choice?

My original question was: if I want the optimal architecture, what should I do, what should I use? Mobx or Recoil?

Well, if you already have a React app with hooks, you may iterate and implement Recoil pretty easily. Please note that Recoil is still experimental.

If you start from scratch, it’s up to you. If you want extreme performance, mobx is right for you. Otherwise, you now have more options to consider.

--

--

David Bottiau

Software designer, open-minded, Lean being and UX advocate.