MobX for Application State Management

tyler clark
6 min readAug 22, 2017

--

MobX is a battle tested library that makes state management simple and scalable by transparently applying functional reactive programming (TFRP).

Intro

What is MobX? Who uses MobX in production code? What are the performance implications of MobX?

Well for starters, there are two main principles to MobX:

1. One-Way-Data-Flow (Flux if Desired)

2. Reactive

Item number two is really the bread and butter to MobX. Check out this other article about the why behind MobX!

So who uses it? Well Microsoft is with the new Outlook

One-Way-Flow

First of all, MobX does not claim to be a “Flux” based framework. There are many authors and JS developers out there that will agree that MobX does not qualify as a flux framework. However, there is autonomy to the structure of the data in the application. MobX does offer many tools and add-ons that a user can use in order to make their application more flux-like.

For example, unlike Redux (another popular state management framework) it does not require the initialization of a store. However, one can be constructed if desired.

Also, the use of a dedicated “Action” or “Dispatcher” is not required. But there is a Strict Mode that comes with MobX that throws errors if the “Action” keyword is not used, but again it is not required. The three pillars to MobX’s functionality are:

  1. Observable: data (or state) that is watched and used in the application. Can be almost any type of data structure and is usually updated by user activity. Computed Values, are created from observable(s) and can also be watched data.
  2. Observer: the connection between components/pieces of code and observables. Updates connected code when a used observable updates, making them “smart”.
  3. Action: updates observable state.

It is important to note about the way data is used within MobX as opposed to other frameworks such as Redux. MobX actions and observables naturally mutate rather then returning a new state. Again, there is a lot of flexibility and implicitly of actions and observables used within an application.

Functional and Reactive

MobX provides the use of derived data as opposed to simply acting on data. To break this statement down, there is a differences between connecting code together like a chain and having separate contexts of code that communicate to each other.

When data is connected like a chain, each piece of code is dependent and reacts to each other by default. Just like shaking a chain. Other state management frameworks like Redux use manual subscribers such as the @connect function. This requires the developer to manually add data that other data depends on within this function. Thus separating the contexts and having them “communicate” with each other.

This type of dependance on the developer can cause over-renderings to the component as unnecessary data, which was once needed is no longer require.

Think of each “link” in the chain as smart components. MobX encourages the use of utilizing smart components as much as possible. This philosophy is different than React and Redux. It is true that on a smaller level the less smart components there are the less performant MobX is compared to others such as Redux. On the contrary, when comparing at scale, MobX with many more smart components is actually more performant. I will get more into this later on.

Recap

Before we get our hands dirty with some code, let’s make sure we are on the same page. MobX has some key differences than other state management frameworks:

  • One-way-data-flow — Can follow flux framework if desired
  • Autonomy — i.e middleware not required for async actions
  • Three Main Concepts — State, actions, and side-effects (reactions)… Thats it!
  • Reactive — Use of observables and observers.

Code

To create a simple counter app let’s start with the UI. I will be using React but MobX can be used in other frameworks such as Angular.

View

import React, { Component } from 'react';class App extends Component {render() {   return (     <div>       <button onClick={this.onReset}>          Seconds passed: {this.props.appState.timer}       </button>     </div>   );}onReset = () => {   this.props.appState.resetTimer();}};

The code above shows us the skeleton of our view. A simple component that contains a button that uses props to show the current state of the timer. There is also a function that will call some sort of action/method that we assume, will reset the state of the timer.

In order for this component to derive the addState.timer from props, we need to make it smart. We do this by introducing the @observer keyword. Let’s also add the debugging tool component.

import React, { Component } from 'react';import { observer } from 'mobx-react';import DevTools from 'mobx-react-devtools';@observerclass App extends Component {  render() {    return (      <div>        <button onClick={this.onReset}>          Seconds passed: {this.props.appState.timer}        </button>        <DevTools />      </div>    ); } onReset = () => {  this.props.appState.resetTimer(); }};

Notice the use of the @decorater. We do not need to use this here but is simpler to read. See here for connecting up the observer without the decorator.

State and Actions

import { observable, action } from 'mobx';
export default class AppState { @observable timer = 0; constructor() { setInterval(() => { this.timer += 1; }, 1000); } @action resetTimer() { this.timer = 0; }}

Through the use of JavaScript’s ES6 class syntax, we can create a store-like home for our state. If you look back to the view, we depend on the current state of the “timer”. The glue between a state value and smart, observer component is the use of the @observable. Again the use of a decorator is not required. Then to make the state automatically update we have a setInterval function to continuously change by +1 every second.

Then we’ve created our first action! The resetTimer() function reset’s the class’s timer property to zero.

Now let’s create an index that holds the component and passes the class with the action and state through as props.

Index.js

import React from 'react';import { render } from 'react-dom';import { AppContainer } from 'react-hot-loader';import AppState from './AppState';import App from './App';const appState = new AppState();render( <AppContainer>  <App appState={appState} /> </AppContainer>,document.getElementById('root'));

Since this is a class, we do need to instantiate it. With appState (which holds our class with the action and state) passed through to our App component, we can fire up our bundler and see the magic working.

Performance

This is all nice but if performance is lacking, then it’s not worth it.

Over-rendering tends to be the most expensive drain on performance. This is seen when putting a console.log into your render() of a component in a large code base. There can be many costly re-renders with the return of new data from Ajax calls and other data updates. MobX gets this number down to zero.

Use runtime analysis to determine the smallest possible set of observer → observable relationships. This leads to a computational model where it can be guaranteed that the minimum amount of derivations are run without ever observing a stale value.

For example when 10 components are updated within 10000, the performance of observables really comes to light

https://www.mendix.com/blog/making-react-reactive-pursuit-high-performing-easily-maintainable-react-apps/

This is because the “connection” of data is checked at runtime. So the more “smart” components, the better the ability to check to see if the component needs to re-render. Also, any data that is computed but not used is lazily loaded. Thus only optimizing what is needed, only when they are needed.

The solution is simple: if a computed value is not reactive, it will be evaluated on demand (lazily), just like a normal getter function. Lazy derivations (which never observe anything) can simply be garbage collected if they run out of scope.

Further References

Follow me on twitter! @iamtylerwclark

--

--

tyler clark

Father/Husband, Front-End Dev, Sports Fanatic, Mormon, Sightseer, Car Enthusiast, Ambitious, Always a Student