
Mobx: Simple, scalable state management - lolptdr
https://github.com/mobxjs/mobx
======
lootsauce
There is no way for me to succinctly express how great I think this project is
but let me try. IMHO this is the most sophisticated yet simple solution to
state management for web apps out there. I have worked on several large
Angular and React apps and worked with several flux frameworks. I have also
written my own Flux like architecture seeking to improve and simplify state
management by reducing boilerplate and increasing performance, so I have
thought about all the issues involved a lot.

It is one thing to put together a set of observations like the Flux
architecture that makes a lot of sense and simplifies app development. It is
entirely another thing, as we have seen in the past couple years, to implement
an elegantly simple and clean expression of that. It is deceivingly simple but
in my experience even though it seems to have a bit of magic behind it (which
I generally dislike) Mobservable now MobX is probably the simplest and
ultimate progression of state management for React and other like apps. It is
the reason I stopped development on my own state management system. Once I
envisioned how to remove even more boilerplate even more odd conventions I
came to realize Mobservable achieved it all, and the performance was just as
good too. It really deserves the attention of any serious app developer and
it's not limited to React either.

------
jameslk
I've used both Redux and Mobx in two different large-scale consumer-facing
apps. Mobx has proven to be a lot easier to grasp and much more flexible to
different scenarios. I had to use a lot of hacks to get Redux to play nicely
with different libraries since it treats JavaScript as an immutable world[0]
and Mobx has greatly simplified this issue by providing tools to handle
situations where objects may mutate[1][2].

0\. [http://stackoverflow.com/questions/32352982/how-to-put-
metho...](http://stackoverflow.com/questions/32352982/how-to-put-methods-onto-
the-objects-in-redux-state)

1\.
[http://mobxjs.github.io/mobx/refguide/modifiers.html](http://mobxjs.github.io/mobx/refguide/modifiers.html)

2\.
[http://mobxjs.github.io/mobx/refguide/extending.html](http://mobxjs.github.io/mobx/refguide/extending.html)

~~~
honua
I found it quite the opposite in that I grasped Redux immediately and felt
lost in fog trying to work out using Mobservable

~~~
jameslk
I spent a lot of time trying to wrap my head around certain features of Redux.
For example, all the currying used in various places (especially for
middleware) makes it hard to follow what's going on. Indeed, the author points
out in the guide on middleware tongue-in-cheek, "If your head boiled from
reading the above section, imagine what it was like to write it." Part of my
job is to figure things out and explain it to others. When I have a hard time
understanding things myself, imagine how it's going to go for explaining it to
someone else.

On the other hand, Mobx also did take a bit of time to figure out its API, but
once it was clear, it was very straight forward how to use it and explaining
it to others was also painless. No need for extraneous action-
creators/reducers ceremony and no opinionated way of what kind of data I can
use, or gotchas when that data is mutated.

~~~
danabramov
Well, to be fair middleware is a rather advanced feature and most users don’t
ever need to write one.

Action creators are also a mere convention and are not essential to Redux. You
can dispatch action objects inline if you prefer. Reducers do add some
ceremony, and I wrote up a little on the reasons here:
[https://news.ycombinator.com/item?id=11187727](https://news.ycombinator.com/item?id=11187727)

Glad you found something you like though! No solution is perfect, and it’s all
about the tradeoffs you care about.

~~~
jameslk
Yes, those are fair points. It wasn't really the actions/reducers that were
the biggest trouble as much as the seemingly strict design around immutable-
only data. I actually think that sometimes enforcing some kind of structure
around architecture helps protect it from abuse, and following a more Flux-
like design seems to help more than hurt in that respect. However, it was more
complicated to explain how this worked rather than Mobx's "it works like a
spreadsheet" explanation.

------
ikido
I know that a lot of people like to use different kinds of flux libraries for
their React apps, with Redux having a lot of traction, but you should
definitely look at Mobx.

We've introduced it to one of our project 4 months ago and our state
management became as simple and easy as it should be with React. No
boilerplate subscriptions, no helpers that modify normalized data, no endless
re-renders on every state change and very easy to understand concept for new
developers.

We literally stopped worrying about the state though it took some time to
rewrite the app.

~~~
Keats
i think it would be interesting to help on
[https://github.com/mobxjs/mobx/issues/104](https://github.com/mobxjs/mobx/issues/104)
if you have time

~~~
ikido
Thanks, I'm actually working on it

~~~
Keats
Great to hear!

------
jarpineh
I took a look. This seems intriguing because it doesn't bring the whole RxJS
with it to make observables work. Though I'm a bit baffled by the developer's
statements about immutability of values in his SurviveJS interview [1]:

"With mutable data structures, it is trivial to guarantee that there is only
one version of a certain domain object in memory."

I'd like to know how this mutable values can be tracked while immutable values
can just equality check. I'm currently learning ClojureScript with few simple
projects, and its immutability both pleases me and drives me crazy.

Though I can agree on his point that Flux (and Redux) do take over your
application. I'd definitely like to use something else with my next React
project if possible, and Mobx looks it could fit the bill.

Also, SurviveJS is a good React and Webpack book that's continuously expanded
and adapted to use different libraries (like for Redux and Mobx). You should
buy it!

[1] [http://survivejs.com/blog/mobservable-
interview/](http://survivejs.com/blog/mobservable-interview/)

~~~
mweststrate
"With mutable data structures, it is trivial to guarantee that there is only
one version of a certain domain object in memory."

That statement refers to the fact that you loose (automatic) referential
integrity when you start using Immutable data.

Take the following scenario: you have an app with Users and Tasks. Tasks can
be assigned to users. When you express this using immutable data you have two
problems. The first one is linking tasks to users. If you would link Task1 to
Person1 and after that change the name of Person1, you would get a new person,
Person1v2. However Task1 would still refer to the old, unmodified Person1, so
then you have suddenly two versions of the same person in memory. A fresh and
a stale one. In other words, you lost referential integrity.

Now the usual way to fix is, is to not use real references, but to normalize
data and store only a key to the person. The effect of this is that you have
to always lookup the person related to Task1 again from the state tree. If you
have a reference (variable) to that person somewhere, make sure it is short-
lived because you won't know if you are still referring to the correct one.

The second problem is that when you have correctly normalized your data and
always do fresh lookups, you will still not know _when_ to make these lookups.
If you have a TaskView that renders the name of the related Person, and the
related person changes, the TaskView will not detect this automatically. The
related person is not even a prop of the TaskView component so pushing a new
state tree through your app won't repaint the TaskView if you are using
PureRenderMixin (which is usually the purpose of using immutable state trees
in the first place). For that issue there is a solution as well; you can
introduce selectors (or lenses) that query the state tree to detect if the
relevant person is changed. So now we already have three new concepts (no
refs, normalization, lookups, selectors) to solve a problem that mutable data
doesn't have in the first place.

By using mutable data and observing it using MobX these problems are solved
(imho) more elegantly: references are never stale because if you would modify
Person1, Task1 would still refer to the 'latest' person1, because it is still
the same object. Secondly, because it is the same object, fine grained object
observers can be established automatically for you, making sure the TaskView
gets updated as soon as something relevant changes. This means that you have
less concepts to learn and maintain (no copy-on-write, no need to assign a
unique key to everything, no data normalization, no lookups, no selectors to
make sure your views are always in sync with the state). That saves a
significant amount of boring yet error prone work when your application's
state model becomes more complex than the average Todo app.

So that is the long story behind that short comment. I hope it clarifies the
statement!

~~~
jarpineh
This definitely clarifies things. Thank you for your trouble. With Mobx I'm
not handling a regular object, but a wrapped object (I think). This wrapper
provised the observation capability for getters and updates to observers when
using setters. So, the usage patterns look to be the same than with regular,
unwrapped object, but the Mobx makes sure that when accessed it always gives
the latest value and when updated, the update is messaged to its observers. It
feels a rather clever use of JS getters and setters.

I have an project coming up that's going to integrate several previously
separate apps into one. I can't imagine how I would combine their state into
one with Redux. With observables I could just get the app observe their state
changes and affect changes in others without contained apps being aware of
each other.

~~~
mweststrate
Exactly. Integrating many apps is something we do as well. We have a large
application that handles roughly 500 different domain classes (just scan
through
[https://apidocs.mendix.com/modelsdk/latest/](https://apidocs.mendix.com/modelsdk/latest/)
to see how vast that is).

This approach enables our partners to write plugins that are not only simple
to write, because they can just work with 'plain' javascript classes and
arrays (and don't need to learn the whole, for example, redux architecture),
but which are also really easy to integrate in our visual tools. Our UI stays
always efficiently in sync with _whatever_ mutations plugins make. In an
architecture with explicit subscriptions, events or selectors this would be a
lot harder to achieve.

------
s369610
Nice, looks to me like it covers most of what is proposed in here the
Deprecating Observers paper here:
[http://infoscience.epfl.ch/record/148043/files/DeprecatingOb...](http://infoscience.epfl.ch/record/148043/files/DeprecatingObserversTR2010.pdf)
Slightly different implementation since it uses untracked() instead of .Now to
avoid dependency creation and a nice bridge to the regular observable universe
(they have autorunAsync) but it would be good to have a reactor that can act
as an observable using emit etc and if the observables could be modified with
throttle, or buffer etc.

Edit: I have been enjoying similar reactiveness using ractivejs but it hasn't
managed to split the reactive data from the view logic.

------
rpedela
Wow, looks really interesting!

With Flux/Redux I have control over when and how data is updated, but that is
not the case here. What are the performance characteristics? Is there a limit
to the number of observables before performance suffers?

There is also the question of routing and history. What is the best way to use
observables in a single-page app along with routing and history (i.e working
back button)?

~~~
jesstaa
Performance characteristics are that this increases expsense with the size of
your data where as just re-rendering increases expense with the size of your
UI. It's actually much more common to have lots of data but only render a
small amount of it, so re-rendering is generally cheaper.

The size and complexity of UIs have a natural limit because of screen size and
usability, but the amount of data you use to generate a UI has essentially no
natural upper limit.

Pete Hunt gave a talk on exactly this almost two years ago.
[https://www.youtube.com/watch?v=-DX3vJiqxm4](https://www.youtube.com/watch?v=-DX3vJiqxm4)

~~~
ikido
That's a good point, but in Redux you subscribe to all changes in single state
tree where all data lives. That means that every update of data (which has no
upper limit) will cause your UI to re-render. Sure, you can implement
shouldComponentUpdate, but still it will be called on every state update, plus
you do can do exactly the same with mobx. The difference is that with mobx you
subscribe to changes of certain data, and it happens automatically.

~~~
jesstaa
The talk addresses this. The whole point of Reactjs is that updating the whole
UI isn't very expensive so you don't have to worry about doing fine grained
data change tracking (which is harder to optimise due it not scaling well as
data sizes increase)

~~~
monfera
I'll assume a render tree with only functional components for simplicity's
sake, and no observables, just plain function application:

A full rerendering requires that all the functions be called. Indeed, there's
a natural limit to how much data is updated on the screen (well, unless we use
canvas or WebGL but I digress) but the render functions themselves may be
expensive and wasteful to rerun, especially if there is inferrable knowledge
that the nature of the change doesn't warrant a recalc in some branches. Even
if it doesn't cause jitter, it may be wasteful on mobile battery.

Why do I think render functions may be expensive? Official React documentation
steers people toward having stores that keep 'primary truths' rather than
things that can be derived from them via pure functions; and it suggests that
these calculations be part of the render functions. It works, it's functional
and it's clean. But you potentially run a lot of calculations, depending on
the domain. With plain FP, much of this will be wasted as causing no visible
change. Not only this, but the needlessly executed render functions do
generate virtual DOM snippets; and these snippets do get scheduled for DOM
diffing. All these add up to what is, in my experience, a jank-inducing
difference. I even introduced memoization, but just DOM diffing alone is
significant on a sizeable app (which makes sense as there may be an order or
two magnitude difference between VDOM elements that exist vs. ones that really
may change).

With observables, e.g. reexecuting a calc or regenerating a DOM snippet,
control is finer grained, without the manual and possibly erroneous
performance optimization hinting approach known as shouldComponentUpdate. I
had cases when blind function reapplication (memoized or not) caused jank on
the desktop while observables were smooth even on the mobile.

------
gcb0
seems like a very convoluted and inefficient way to have getter and setters
without saying those words

~~~
coldtea
Convoluted and inefficient? Sounds like you have never heard of observables...
If anything, manually adding getters and setters is what's convoluted.

