- Installation:
npm install mobx --save
. React bindings:npm install mobx-react --save
- Api documentation (including ES5, ES6, Typescript examples): https://mobxjs.github.io/
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.
which includes the UI, data serialization, server communication, etc.
If React allows you to declaratively define your component tree and uses the virtual DOM as black box to minimize the number of DOM mutations, then MobX is the library that allows you to define your state model and derivations in a declarative manner while minimizing the number of computations. MobX maintains a virtual dependency graph to ensure that your derivations will never be stale nor executed more often than strictly needed.
To use install pre- 2.0 mobx*
compatible packages, use mobservable
instead of mobx
.
See the changelog for all the details.
MobX has a only few core concepts. The following snippets can be tried online using JSFiddle.
MobX adds observable capabilities to existing data structures like objects, arrays and class instances. This can simply be done by annotating your class properties with the @observable decorator (ES2015), or by invoking the observable
or extendObservable
functions (ES5). See Language support for language-specific examples.
class Todo {
id = Math.random();
@observable title = "";
@observable finished = false;
}
Using @observable
is like turning a value into a spreadsheet cell. But unlike spreadsheets, these values can be not just primitive values, but references, objects and arrays as well. You can even define your own observable data sources.
With MobX you can simply define derived values that will update automatically when relevant data is modified. For example by using the @computed
decorator or by using parameterless functions as property values in extendObservable
.
class TodoList {
@observable todos = [];
@computed get unfinishedTodoCount() {
return this.todos.filter(todo => !todo.finished).length;
}
}
MobX will ensure that unfinishedTodoCount
is updated automatically when a todo is added or when one of the finished
properties is modified.
Computations like these can very well be compared with formulas in spreadsheet programs like MS Excel. They update automatically whenever, and only when, needed.
Reactions are similar to a computed value, but instead of producing a new value, a reaction produces a side effect for things like printing to the console, making network requests, incrementally updating the React component tree to patch the DOM, etc. In short, reactions bridge reactive and imperative programming.
If you are using React, you can turn your (stateless function) components into reactive components by simply adding the @observer
decorator from the mobx-react
package onto them.
import React, {Component} from 'react';
import {observer} from "mobx-react";
@observer
class TodoListView extends Component {
render() {
return <div>
<ul>
{this.props.todoList.todos.map(todo =>
<TodoView todo={todo} key={todo.id} />
)}
</ul>
Tasks left: {this.props.todoList.unfinishedTodoCount}
</div>
}
}
const TodoView = observer(({todo}) =>
<li>
<input
type="checkbox"
checked={todo.finished}
onClick={() => todo.finished = !todo.finished}
/>{todo.title}
</li>
);
const store = new TodoList();
React.render(<TodoListView todoList={store} />, document.getElementById('mount'));
observer
turns React (function) components into derivations of the data they render.
Also, reactions can be created using the autorun
, autorunAsync
or when
functions to fit your specific situations.
When using MobX there are no smart or dumb components.
All components render smartly but are defined in a dumb manner. MobX will simple make sure the components are always re-rendered whenever needed,
but also no more than that.
So the onClick
handler in the above example will force the proper TodoView
to render,
and it will cause the TodoListView
to render if the number of unfinished tasks has changed.
However, if you would remove the Tasks left
line (or put it into a separate component), the TodoListView
will no longer re-render when ticking a box.
You can verify this yourself by changing the JSFiddle.
Unlike many flux frameworks, MobX is unopinionated about how user events should be handled.
This can be done in a Flux like manner.
Or by processing events using RxJS.
Or by simply handling events in the most straightforward way possible, as demonstrated in the above onClick
handler.
In the end it all boils down to: Somehow the state should be updated.
After updating the state, MobX
will take care of the rest, in an efficient, glitch-free manner.
So simple statements like below are enough to automatically update the user interface.
There is no technical need for firing events, calling dispatcher or what more.
A React component is in the end nothing more than a fancy representation of your state.
A derivation that will be managed by MobX.
store.todos.push(
new Todo("Get Coffee"),
new Todo("Write simpler code")
);
store.todos[0].finished = true;
MobX is one of the least obtrusive libraries you can use for state management. That makes the MobX
approach not just simple, but very scalable as well:
With MobX you don't need to normalize your data. This makes the library very suitable for very complex domain models (At Mendix for example ~500 different domain classes in a single application).
Since data doesn't need to be normalized, and MobX automatically tracks the relations between state and derivations, you get referential integrity for free. Rendering something that is accessed through three levels of indirection? No problem, MobX will track them and re-render whenever one of the references changes. As a result staleness bugs are a thing of the past. As a programmer you might forget that changing some data might influence an seemingly unrelated component in a corner case. MobX won't forget.
As demonstrated above, modifying state when using MobX is very straightforward. You simply write down your intentions. MobX will take care of the rest.
MobX builds a graph of all the derivations in your application to find the least number of re-computations that is needed to prevent staleness. "Derive everything" might sound expensive, MobX builds a virtual derivation graph to minimize the number of recomputations needed to keep derivations in sync with the state. In fact, when testing MobX at Mendix we found out that using this library to track the relations in our code is often a lot more efficient then pushing changes through our application by using handwritten events or "smart" selector based container components. The simple reason is that MobX will establish far more fine grained 'listeners' on your data then you would do as a programmer. Secondly MobX sees the causality between derivations so it can order them in such a way that no derivation has to run twice or introduces a glitch. How that works? See this in-depth explanation of MobX.
MobX works plain javascript structures. Due to it's unobtrusiveness it works with most javascript libraries out of the box, without needing MobX specific library flavors.
So you can simple keep using your existing router-, data fetching and utility libraries like react-router
, director
, superagent
, lodash
etc.
For the same reason you can use it out of the box both server- and client side, in isomorphic applications and with react-native.
The result of this is that you often need to learn less new concepts when using MobX in comparison to other state management solutions.
MobX is inspired by reactive programming principles as found in spreadsheets. It is inspired by MVVM frameworks like in MeteorJS tracker, knockout and Vue.js. But MobX brings Transparent Functional Reactive Programming to the next level and provides a stand alone implementation. It implements TFRP in a glitch-free, synchronous, predictable and efficient manner.
A ton of credits for Mendix, for providing the flexibility and support to maintain MobX and the chance to proof the philosophy of MobX in a real, complex, performance critical applications.
And finally kudo's for all the people that believed in, tried and validated MobX.
- Full API documentation
- Five minute, interactive introduction (with just ES5)
- Boilerplate and example projects for ES5, Babel, Typescript can be found in the MobX organization on github.
Blogs & Videos:
- Reactive 2015 Conference Talk on MobX (mobservable): React, transparent reactive programming and mutable data structures
- Making React reactive: the pursuit of high performing, easily maintainable React apps
- Becoming fully reactive: an in-depth explanation of Mobservable
- Pure rendering in the light of time and state
- SurviveJS interview on MobX, React and Flux
Elegant! I love it! ‐ Johan den Haan, CTO of Mendix
We ported the book Notes and Kanban examples to Mobservable. Check out the source to see how this worked out. Compared to the original I was definitely positively surprised. MobX seems like a good fit for these problems. ‐ Juho Vepsäläinen, author of "SurviveJS - Webpack and React" and jster.net curator
Great job with MobX! Really gives current conventions and libraries a run for their money. ‐ Daniel Dunderfelt
I was reluctant to abandon immutable data and the PureRenderMixin, but I no longer have any reservations. I can't think of any reason not to do things the simple, elegant way you have demonstrated. ‐David Schalk, fpcomplete.com
More testimonials from people using MobX in production can be found on medium: functional reactive flux blog, hacker news, reddit 1, reddit 2 or on twitter under the #mobx and #mobservable hashtags.
- Feel free to send pull requests.
- Use
npm test
to run the basic test suite,npm run coverage
for the test suite with coverage andnpm run perf
for the performance tests.