Superpower Your React App with MobX

October 26, 2018

State management can be a hard thing to do.

When building React apps, Redux is probably the most popular state management toolkit on the market. Although an excellent library, sometimes it can be tedious just to wire everything up and get started with Redux — there’s no magic, only pure hard science.

If you are frustrated with the constant need of setting up boilerplates for Redux, be sure to check out MobX! This short article will give you an overview of how to start using MobX in your React project.

MobX === Magic

State” describes the current status of an app. Changing the state causes the app to react, update UI or send out messages. If you are not careful mutating this status, your app will simply run out of control.

While Redux makes state management less error-prone by making sure that every version of the state is immutable, only allowing updated state versions to be created via a reducer - kind of like a snapshot engine; MobX lets you make changes to the state directly.

Wait, how can MobX be safe if it allows you make changes to the state directly???

Here’s where the magic happens - MobX keeps track of every teeny little state change is very carefully. You just need to tell it in advance which fields or variables need to be tracked, then define some reaction procedures you want MobX to run automatically upon state changes, eg. rerender a React component. Magic doesn’t make mistakes, so you can rest assured that the autorun will not be invoked twice, or too late. On the user’s side, the whole process is as simple as typing a few annotations; behind the scene, that’s because of the 45KB pure MobX magic Redux doesn’t have.

Telling MobX What’s What

OK then! So how do we use MobX, and how do we tell it what we want it to keep track of? In modern JS, we can do that by using decorators (see this in-depth post). Here are five of the most handy decorators you would normally use in MobX.

class Store {
  // decorator 1
  @observable radian = 0

  // decorator 2
  @action setRadian = radian => (this.radian = radian)

  // decorator 3
  @computed
  get degree() {
    return this.radian * (180 / Math.PI)
  }
}

// decorator 4 & 5
@inject('store')
@observer
class Angle extends React.Component {
  /* cool stuff... access the store via `this.props.store` */
}

class App extends React.Component {
  render() {
    return (
      <Provider store={new Store()}>
        <Angle />
      </Provider>
    )
  }
}

Well, the first 3 decorators are imported from the mobx library and can be in any JS apps, whereas the last 2 are specific for binding MobX with React.

@observable is used to annotate what we see as states and needs to be tracked - in this case the radian class property.

@action makes it clear that the only purpose of this function here is to mutate the state. If we enable “MobX strict mode”, functions not annotated with @action will not be allowed to change the state.

@computed makes sure MobX do some investigation and treat degree simply as a separate field/variable/property and cache it like the radian above. Without this annotation, MobX will reevaluate degree every time even it’s only other observables are changed, because get degree is just a normal function call to MobX’s eyes. (See this comment by Michel Weststrate).

In the mobx-react binding package, we also have @observer to annotate React components which we want to be rerendered upon observed changes. The @inject decorator injects the store object passed in the context into the component it wraps as a prop for easy usage. The Provider component is also part of mobx-react.

Note that if you are using create-react-app, decorators are not supported automatically. You will have to npm run eject, npm i -D babel-plugin-transform-decorators-legacy then add this to your babel config inside package.json:

"babel": {
  // blah blah blah
  "plugins": [
      "transform-decorators-legacy"
    ],
  // blah blah blah
  }

You may also need to set ”experimentalDecorators” to true here if you are using VS Code or TypeScript.

Enough Talk, Let’s Build a Time Bomb!

I have created a small “time bomb” app to demonstrate how MobX can be used in practice. Everything is crammed into a single .js file for easier reference. Any suggestions would be appreciated!

Conclusion

In comparison to Redux, MobX is almost 10x bigger in size, but packed with magic spells to simplify your state management workflow, thus saving you precious brain cycles. Try it out in your next project — even if you don’t end up using it in production, it’s always great to learn about other paradigms.

For a more in-depth understanding, definitely go check out the excellent official docs, as well as these Medium posts by Michel - the creator of MobX himself!