- 8 minutes read
Are you looking for a state-management framework without the boilerplate code? You'll love MobX!

MobX in a nutshell

MobX allows you to watch class attributes:

export class Person { @observable age: number; }

When an attribute changes, this may trigger a cascade of follow-up actions:

Computed values are derived from the observed attributes. Usually, these values are defined by a formula like so:

@computed get isAdult(): boolean { return this.age >= 18; }

Reactions are triggered by the change.

when(() => person.age >= 18, () => console.log("You're grown-up now."));

Does this remind you of the $watch of AngularJS 1.x? Or of RxJS observables? Right you are!

But fear not: MobX takes the sting out of it. It doesn't force you to learn to program again, as RxJS does. Actually, you've already seen the worst of it. And it didn't hurt, did it?

Simplicity

Most of the time, MobX is simple and straightforward. You don't have to learn an arcane syntax. Just add the @observable decorator to your class attributes and convert functions of these @observable attribute into @computed getters. That covers 90% of your code.

You've also seen reactions already. That's a slightly more sophisticated syntax. The good news is you can ignore them for the moment. Reactions are an advanced topic. Their primary use-case is I/O. React.js programmers have to annotate their components with a @observer decorator. Angular developers don't even have to do that. Most of the time, Angular's change detection does the trick just as well.

Adding actions to the equation

Until now, MobX is remarkably passive. If you're familiar with Redux, you'll probably miss the actions.

They're there, of course. Almost every application with a UI has a user triggering actions. Granted, sometimes actions aren't triggered by human beings. But almost always there's some agent changing data. Otherwise, it's a pretty dull application.

The key word is "changing data." Many other state management frameworks like Redux forces you to make the actions explicit. So does the strict mode of MobX. Outside the strict mode, it's up to you to make your actions explicit or not.

Be that as it may, declaring an action is simply a matter of adding an @action decorator to a method. Not a big deal.

Recommended architecture of MobX

The MobX documentation has a great image describing the concepts:

That's the closest thing to a "best practice" architecture I've found so far. It's influenced by Redux. Actually, the image indicates there's almost no difference between Redux and MobX. However, as we've seen, the programming model is a lot simpler.

So maybe that's one of the things I consider attractive about MobX. It's like Redux, just more straightforward. Sometimes cherry-picking is both allowed and fun.

MobX ain't opinionated

As to my perception, MobX doesn't impose many restrictions on your architecture. That's a blessing and a burden at the same time. MobX gives you the freedom to shoot yourself in the foot. It's a sharp knife. And sharp knives have the nasty habit of cutting things without thinking twice. Sometimes it's you who's at the sharp end.

I suppose that's the lure of frameworks like Redux and ngrx. They offer the safety net of battle-proven "best practices" for a lot of boilerplate code.

MobX does it the other way round. It assumes you've got a smart team or an architect acting as a benevolent dictator. As long as you're proceeding carefully, MobX allows you to get rid of tons of useless boilerplate code.

A relaxed and declarative programming model

Actually, there's more. You can adopt it without having to give up object-oriented programming. That's a big difference to Redux, which forces you to adopt functional programming whole-heartedly.

For some reason, the MobX introduction claims that MobX "transparently" employs " functional reactive programming (TFRP)". That's true in that functions play an important role in MobX applications, and it reacts to events by calling these functions.

For example, @computed attributes are pure functions. That's great because it's easy to test functions automatedly.

However, my impression is that MobX follows the declarative programming paradigm. And I've already mentioned also object-oriented. For technical reasons, the magic of MobX doesn't work without objects. That's good news for people like me who're already familiar with object-oriented programming.

The declarative programming paradigm keeps fascinating me. Do you know persistence frameworks like Hibernate? If so, you're familiar with the idea. Traditionally, programmers told the computer how to store, load, and find data from a database. With Hibernate, you just say to the computer which objects belong to which tables, and which attributes belong to which database tables. Once you've done that, you leave it to the computer to figure out how to implement the communication with the database.

Pascal's triangle with MobX

Pascals triangleI've uploaded a test application to GitHub illustrating the idea. It's an implementation of Pascal's triangle. My test program is far from being the prototypic MobX application. Primarily, I wrote it to test the performance of MobX. But there's one thing is shows very nicely: it declares the triangle declaratively. Basically, the algorithm below is more or less the standard definition. Each number is the sum of two cells above. If there's only one parent, the number is 1. export class MobxCell { @observable leftParent: MobxCell; @observable rightParent: MobxCell; @computed get value(): number { if (this.leftParent && this.rightParent) { return this.leftParent.value + this.rightParent.value; } if (this.leftParent) return 1; if (this.rightParent) return 1; return 0; } }

Your application works like a spreadsheet

Notice that there's no for loop iterating over all rows and columns. Truth to tell, I needed such a doubly nested loop to construct the data structure of the triangle. Once the data structure is there, you don't have to tell MobX how to calculate the cell values. It figures that out itself. It's a bit like putting formulas in a spreadsheet. Even if there are dozens or hundreds of formula, Excel still knows how to calculate the result. You just declare the formula and leave the hard work to the computer.

MobX performance

MobX is also surprisingly fast.

I've met people claiming "MobX doesn't scale." So I wrote the little number-crunching application I've already mentioned. Granted, number-crunching isn't the traditional use-case for JavaScript. Let alone number-crunching in a data structure containing a lot of reference to other objects. But it's the easiest way to bring a framework like MobX to its limits. On my machine, MobX copes nicely with 10.000 objects. When I raised the bar to 50.000 observed values and computed values, things started to get sluggish. I also tried to time the calculation. That's a bit difficult because MobX has a smart caching strategy. So I don't know if my performance figures are worth anything. They indicate a factor 10 performance penalty.

At first glance, factor 10 sounds shocking. Mind you, that's the difference between a month and a year.

However, in most applications, the performance penalty should be negligible. Remember, I chose number crunching, using the addition because it's the fastest operation. Real-world applications are more complex, involve less observed values, less computed values, and spend more time calculating the values. In other words: While MobX obviously doesn't make your application faster, in most cases, the performance penalty is outweighed by the increased developer productivity.

Not to mention the caching strategy. In some cases, MobX may make your application faster because it only calculates the attributes it really needs. I've even seen reports on the web claiming that MobX is faster than Redux or RxJS in their project. Neither NgRx nor RxJS comes for free.

Wrapping it up

Here's the catch: I haven't used MobX in a major application yet. Neither have I used NgRx in an enterprise-scale application yet. Nonetheless, I consider the programming model of MobX very attractive. Compared to Redux, it's minimally intrusive. I can use a professional-level, battle-proved state management framework without having to learn how to program again. Plus, you can optionally use a relaxed, declarative approach to programming. Your application works like a spreadsheet.

To me, MobX looks like the perfect companion to Angular. Of course, MobX has a much broader scope. You can use it with React.js, Vue.js, and even with server-side Node.js applications.


Comments