Modelling MobX state as in Redux
MobX is easily my favourite JS library. There’s no library out there which would excite random people as much as MobX does. It’s slowly catching up to Redux’s popularity, but it isn’t a silver bullet to all your state management problems. Most troubling is a lack of recommended practice. It doesn’t provide any recipe how to model your state and how to modify it, unlike Redux. Redux has really just one way how to define your state and how to modify it. There are countless boilerplates and 99% have the same code. The only place where redux gives you a bit of leeway is in choosing how to handle your async actions, but that is a different story, because this post is about MobX.
Too much freedom?
MobX allows more freedom-the only important bit is that you use standard assignment operator and mark your state as “observable”, your components as “observer”s. That sounds great, but it leaves most devs wondering-so how do I structure my code? There are many examples listed on MobX docs. Most of them structure the code as any OOP programmer would-into classes. Get a glance of a sample class taken from contact list app Michel Weststrate did for React Amsterdam 2016:
While this style is endorsed by the creator and a big part of the MobX community, there are many other valid styles. Writing classes makes your state hard to serialize and deserialize. This is the worst part about modelling your state into OOP style classes.
Your state needs to be serialized. There’s no way around that-whether it’s for sending over the wire or storing on hard disk, you have to serialize it. Redux is very strict in this-your state always is just a POJO object without any fancy stuff so you can serialize very easily. This fact enables redux to do all that time-travelling, hot reload magic without any hiccup. This also makes Redux easy to test. MobX being so lenient-maybe we could approach our store the same and have the same benefits as Redux’s store has, no?
So that is a core idea I started this whole writeup:
You can have all the benefits of redux with MobX, if you stick all data(state) as observable POJO into singleton store
To prove my claim, I forked the Michel Weststrate’s contact list, changed style to standard.js and rewrote with a singleton global store. Got rid of all state related classes. Couple of hours refactoring later, I’ve pushed last changes into https://github.com/capaj/mobx-contacts-list and voila-here’s the resulting god store(without http request bits):
The functionality should be 100% same as the original app. Feel free to clone and npm start it yourself. Component themselves didn’t change much-only event handlers changed. Instead of calling a method on a class, they have the method code inlined now. Best showcase is probably a contact view component. It’s not a problem for such a small and simple app.
I hope by that, I proved my initial claim. I should point out that you lose the great benefit of MobX if you do this-references.
References. References cannot be in a store which is serialazable by default. So writing MobX like Redux means sacrificing them.
Basically you can’t have an object reference in your state, so the only way is to store ids. Of course you need to find items by their identifier in a collection every time you’ll be looking for an item. Just like in Redux with reselect.
As soon as you have a lot of relations(references) between separate instances of your data, it is better to sacrifice serialazability. Serialization and deserialization is usually easier done than working with data without referential integrity. Also you typically serialize/deserialize data much less often than you need to reference data in your views or in your app. There are tools for serialization, which help tremendously-serializr for example. I haven’t tried it, but I may explore it in my next post.
The refactored version is roughly 150 lines lighter than the original, but that’s not very important. I think the most important is readability and how fast you can add new features. By this I’ve come to conclusion that for bigger apps, keeping to the old-school classy style of programming is indeed the best way to write MobX apps, just like the official samples showcase. Mobx is scalable thgough-you don’t have to have all your classes in place to start using it. It scales as needed.
Serializable-single global store is certainly also a valid pattern for MobX, but I believe for larger teams, classes and their instances will be much more manageable. Just don’t use inheritance too much and you’ll be fine :D