Leonardo Maldonado Full Stack Developer. JavaScript, React, TypeScript, GraphQL.

What’s new in MobX 6.0

4 min read 1180

There are a lot of requirements needed to make a library well known and commonly used in React. Especially with the state-management libraries, the core concepts that the library is built upon can make a total difference in whether developers are going to adopt the framework or not.

One of the reasons why MobX was adopted by the community and used in a lot of different projects is because of its philosophy. A very straightforward, simple, robust, and unopinionated library, MobX is only getting better as time passes. A state-management library that is almost boilerplate free makes the life of developers easier, and that’s one of the major advantages of MobX over other conventional state-management libraries in React.

MobX is one of the most used state-management libraries in React and it now has a new version, introducing a few changes in order to make the library more simple, robust, and scalable. MobX is not a library exclusive for React apps, it can be used with other JavaScript libraries and frameworks as well.

We will explore what’s new in the MobX 6.0 version and see how we can migrate our code from older versions to the newest version.

MobX

In case you haven’t tried out MobX yet, I will show you the concepts that you should have in mind about how MobX really works. There are a lot of advantages of using MobX in React applications in order to make it more scalable, robust, and straightforward. From the docs.

Anything that can be derived from the application state, should be. Automatically.

Basically, MobX has four important concepts that you should have in mind:

Observables are responsible for keeping track of data structures (classes, objects, arrays, references). Whenever a value in our store changes, MobX will keep track of the new value for us:

import { observable } from "mobx";
class Store {
  @observable counter = 0;
}

Actions are responsible for modifying our state. Whenever we want to update a value, we need to perform an action. Actions always happen in response to an event, a button clicked or a form submitted, for example:

import { observable } from "mobx";
class Store {
  @observable counter = 0;
  @action increment() {
    this.counter++;
  }

  @action decrement() {
    this.counter--;
  }
}

Actions are responsible for changing and modifying our observables. In MobX, by default, there’s no way to update the state outside an action. This rule makes your codebase and state more predictable, bug-free, and robust.

Computed values are used for deriving information for observables. They are a function that keeps track of your state, whenever it changes, their returned values will change as well:

We made a custom demo for .
No really. Click here to check it out.

import { observable } from "mobx";
class Store {
  @observable counter = 0;
  @action increment() {
    this.counter++;
  }

  @action decrement() {
    this.counter--;
  }

  @computed counterMoreThan10() {
    return this.counter > 10;
  }
}

Reactions are pretty similar to computed values but the difference is that it triggers side-effects and occurs when observables change, the goal of having reactions is to perform side-effects automatically.

Now that we know a little bit about the core concepts of MobX, let’s understand what has really changed in the 6.0 version and how its API has become easier and powerful to use.

MobX 6.0

A few months ago the proposal of MobX 6 was created by the creator of the library Michel Weststrate. As many proposals for new versions, there was a lot of discussion about what should be included and what should be dropped in the new upcoming version of MobX.

These are the changes and what’s new in the new MobX 6.0:

Decorators

A lot of projects are known for making use of JavaScript decorators, such as Angular, NestJS, and of course MobX. Decorators are currently at stage 2 proposal and it doesn’t seem like it will be supported anytime soon.

One of the most debated points in the new 6.0 version was if decorators should be dropped in order to gain a few advantages like:

  • More compatibility with standard modern JavaScript
  • Work out of the box in most setups (e.g. create-react-app)
  • Reduce bundle size

Decorators are not required in MobX anymore, although the library still could be used without decorators in the previous versions, a lot of developers didn’t like MobX at first just because the use of decorators.

For example, this is an example of MobX in the previous versions making use of decorators:

import { observable } from "mobx";
class Store {
  @observable counter = 0;
  @action increment() {
    this.counter++;
  }

  @action decrement() {
    this.counter--;
  }

  @computed counterMoreThan10() {
    return this.counter > 10;
  }
}

This is an example of MobX in the 6.0 version:

import {
  observable,
  action,
  computed,
  makeObservable
} from "mobx";
class Store {
  counter = 0;
  constructor() {
    makeObservable(this, {
      counter: observable,
      increment: action,
      decrement: action,
      counterMoreThan10: computed
    });
  }
  increment() {
    this.counter++;
  }
  decrement() {
    this.counter--;
  }
  get counterMoreThan10() {
    return this.counter > 10;
  }
}

For some developers, the syntax without decorators is cleaner and simpler, for others, the developer experience got worse.

The fact is that the makeObservable utility function makes the integration easier, there’s no longer a need to download and use a lot of different packages just because of decorators.

The makeObservable utility function should be wrapped inside the constructor method and for each property of the state, in order to make it observable, should be specified using an annotation:

constructor() {
  makeObservable(this, {
    counter: observable,
    increment: action,
    decrement: action,
    counterMoreThan10: computed
  });
}

Decorators can still be used in MobX, but we will still need to add a constructor to the class, use the makeObservable utility function and we can omit the second argument:

class Store {
  @observable counter = 0;
  constructor() {
    makeObservable(this);
  }
  @action increment() {
    this.counter++;
  }
  @action decrement() {
    this.counter--;
  }
  @computed get counterMoreThan10() {
    return this.counter > 10;
  }
}

makeAutoObservable

Introduced in the 6.0 version, the makeAutoObservable utility function is way more powerful than the makeObservable utility function.

It makes all properties observable by default, although you still can override with specific annotations:

class Store {
  counter = 0;
  constructor() {
    makeAutoObservable(this);
  }
  increment() {
    this.counter++;
  }
  decrement() {
    this.counter--;
  }
  get counterMoreThan10() {
    return this.counter > 10;
  }
}

The makeAutoObservable is really one of the most important and awesome changes in the new version of MobX. It makes things easier for developers, removing the need to mention each observable, action, and computed value that they might have inside a store.

Proxies

MobX has a few really different ways for configuration and in order to use it in some JavaScript engines, there are a few things to be done.

In MobX 5.0, support for proxies was required and this requirement was causing a problem of support in some JavaScript engines, especially for Internet Explorer and React Native (depending on the engine).

Under the hood, MobX uses proxies to make arrays and plain objects observable. The problem is that it can cause some problems for some JavaScript engines that don’t support proxies. In the MobX 6.0, proxies are still supported and required but now there’s a way to disable it by using the configure function:

import { configure } from "mobx";
configure({     
  useProxies: "never"
})

Conclusion

It’s not an easy job to maintain and keep updating a library for many years, and the MobX community deserves the credit for it. The library keeps getting better and better and following its philosophy since the beginning, “Anything that can be derived from the application state, should be derived. Automatically”.

Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — .

Leonardo Maldonado Full Stack Developer. JavaScript, React, TypeScript, GraphQL.

Leave a Reply