To take a look at MobX State Tree, we should first understand some basics of MobX.

MobX simplifies state management in comparison to Redux. It is based on an idea that anything that can be derived from the application state should be derived from it, automatically.

There are four core concepts in MobX:

  • Observables (they turn data structures into streams of data (example: ShoppingCard array, when we push the new item to an array, the observers to this data get changes)
  • Computed values (they derive from application state, are pure and are automatically updated (example: TotalCostOfShoppingCart value)
  • Reactions (similar to computed values, they produce a side effect, for example, MobX has a custom reaction called autorun, which is called every time the state changes)
  • Actions (they modify state, example: an action of adding a new item to the shopping cart)

You can upgrade your MobX setup by adding a library called Mobx State Tree. It is a state container that allows us to manage data in our application using models and types. It’s very opinionated about how data should be structured and updated.

MST state is presented as a living tree and each branch includes type information and state.

You can install the NPM package by either calling npm install mobx-state-tree or yarn add mobx-state-tree.

Model and Types

MSTs strong feature is the support for different types (array, map, maybe, refinements and unions). It performs runtime type checking and supports defining optional types, default values for types and many more.

Example of defining a Book model with an id, title and is_read boolean. As you can see, we provide a name of the property (title and ‘’, which is a default value and a shorthand for types.optional(types.string, ‘’). Learn more at https://github.com/mobxjs/mobx-state-tree#types-overview.

const Book = types
.model("Book", {
 id: types.identifier(),
 title: "", 
 is_read: false
})

You can also link to another model by a reference:

const Book = types
.model("Book", {
 cover: types.reference(BookCover)
})

Views

You can define views, which are similar to computed values in MobX, by returning an object literal with specified functions. The instance of the model will be passed as a first argument (self) and we don’t need to worry about bounding methods.

.views(self => ({
 get readBooksCount() {
  return self.books.filter(book => book.is_read).length;
 },

 get unreadBooksCount() {
  return self.books.filter(book => !book.is_read).length;
 }
}))

Actions

Primary objective of actions is modification of state (similar to views, you get reference to MST model instance through self).

.actions(self => ({
 addBook(title) {
  const id = (self.books.length + 1).toString();
  self.books.push({ id, title });
 }
}))

Similar to React, MST also supports some lifecycle hooks, which often come in handy like afterCreate, beforeDestroy, etc.

Example

Here is an example of MobX State Tree. I took our previous MobX CodeSandbox and rewrote it to use MST as well. The heart of application (the store) looks really similar to plain MobX store. I created an instance of MST store in index.js and passed it in provider.

Alternatives

MobX State Tree is a great opinionated choice for managing state in the Mobx + React setups. By baking in runtime type checking it is however a bit slower than vanilla MobX or React’s setState. If you are building a performance-oriented app, you need may need to explore alternatives, like using just MobX and TypeScript.

Written by: Janez Čadež

Follow him on GitHub https://github.com/jamzi
Check out his Blog https://devhealth.io
Follow him on Twitter @jamziSLO