MobX gotchas

Friday, Apr 14 2017 in react javascript

To decide which technologies to cover React book, I’ve developed the same example with Redux and MobX. While I ended up with less code with MobX thatn with Redux, I’ve found out that structuring an application with MobX still requires rethinking your data structures compared to a plain React application.

get() and set() everywhere

One advantage of React is that you can think about each component separately. Define the props that you need for a section of the interface and you’re good to go. Then you can think about how to handle state and how the props you pass to a component map to a larger structure.

Mobx, in contrast, marks all your variables. For example, if you wish to make a date observable so that the UI automatically re-renders when you change the date, you need to call get() and set() all the time:

const selectedDate = observable(new Date());
selectedDate.set(<whatever new date>);
selectedDate.get() // get the currently selected date

Of course, whether some data is an observable or not is not apparent at first sight, so if you use the data far away from where you defined it (by passing it as a prop to a React component), you might end up into trouble.

data.property everywhere

You can work around this if you define all your data as properties on an object: if you make a whole object observable, then you can set and get properties with the usual syntax:

const data = observable({selectedDate: new Date()});

Now you can do:

data.selectedDate = date;

Since get() and set() are annoying, in practice MobX pushes you to define all your data as part of an object, and you get and set its properties.

In the context of a React application, when you pass data down to components, you also need to pass the whole object, otherwise MobX won’t register changes to the properties (see dereference values late in the official MobX documentation). Every component needs to be aware of the structure of the parent object of the data it uses, which makes re-using components difficult and requires you to modify more code if your data structure changes.

You also understand less easily what a component does by looking at its props, since more component end up consuming a whole object.

Classes everywhere

You can mitigate this if you make parts of the state tree reactive on their own by defining them as class instances: for example, instead of updating a movie array containing plain movie objects, you can make MobX track each movie object at an individual level by by making each movie an instance of a Movie class that uses MobX annotations:

import { observable } from 'mobx';

export default class Movie {
   @observable
  favorite = false;
  constructor(title, date) {
    this.date = date;
    this.title = title;
  }
};

Since MobX favours classes, you end up with a natural place for your type definitions, so MobX plays well with TypeScript and Flow.

Still, types don’t tell you which values are tracked and which not at the usage point, which is what I like about setState() in plain React: your data has no special properties.

If you prefer working with plain data structure and returning new values instead of mutating fields, MobX might not work for you, but it does seem reduce the amount of line of codes compared to Redux, maybe at the expense of clarity.