Philosophy

mobx-state-tree is a state container that combines the simplicity and ease of mutable data with the traceability of immutable data and the reactiveness and performance of observable data.

Simply put, mobx-state-tree tries to combine the best features of both immutability (transactionality, traceability, and composition) and mutability (discoverability, co-location, and encapsulation) based approaches to state management; everything to provide the best developer experience possible. Unlike MobX itself, mobx-state-tree is very opinionated on how data should be structured and updated. This makes it possible to solve many common problems out of the box.

Central in MST (mobx-state-tree) is the concept of a living tree. The tree consists of mutable, but strictly protected objects enriched with runtime type information. In other words, each tree has a shape (type information) and state (data). From this living tree, immutable, structurally shared snapshots are generated automatically.

import { types, onSnapshot } from "mobx-state-tree"

const Todo = types.model("Todo", {
    title: types.string,
    done: false
}).actions(self => ({
    toggle() {
        self.done = !self.done
    }
}))

const Store = types.model("Store", {
    todos: types.array(Todo)
})

// create an instance from a snapshot
const store = Store.create({ todos: [{
    title: "Get coffee"
}]})

// listen to new snapshots
onSnapshot(store, (snapshot) => {
    console.dir(snapshot)
})

// invoke action that modifies the tree
store.todos[0].toggle()
// prints: `{ todos: [{ title: "Get coffee", done: true }]}`

By using the type information available, snapshots can be converted to living trees, and vice versa, with zero effort. Because of this, time travelling is supported out of the box, and tools like HMR are trivial to support.

The type information is designed in such a way that it is used both at design- and run-time to verify type correctness. (Design time type checking works in TypeScript only at the moment.)

Because state trees are living, mutable models, actions are straight-forward to write; just modify local instance properties where appropriate. It is not necessary to produce a new state tree yourself, MST's snapshot functionality will derive one for your automatically.

Although mutable sounds scary to some, fear not: actions have many interesting properties. By default, trees can only be modified by using an action that belongs to the same subtree. Furthermore, actions are replayable and can be used to distribute changes.

Moreover, because changes can be detected on a fine-grained level, JSON patches are supported out of the box. Simply subscribing to the patch stream of a tree is another way to sync diffs with, for example, back-end servers or other clients.

Since MST uses MobX behind the scenes, it integrates seamlessly with mobx and mobx-react. But even cooler: because it supports snapshots, middleware and replayable actions out of the box, it is even possible to replace a Redux store and reducer with a MobX state tree. This make it even possible to connect the Redux DevTools to MST.

Finally, MST has built-in support for references, identifiers, dependency injection, change recording and circular type definitions (even across files). Even fancier: it analyzes liveliness of objects, failing early when you try to access accidentally cached information!

A pretty unique feature of MST is that it offers liveliness guarantees; it will throw when reading or writing from objects that are no longer part of a state tree. This protects you against accidental stale reads of objects still referred by, for example, a closure.

const oldTodo = store.todos[0]
store.removeTodo(0)

function logTodo(todo) {
    setTimeout(
        () => console.log(todo.title),
        1000
    )
)

logTodo(store.todos[0])
store.removeTodo(0)
// throws exception in one second for using an stale object!

Another way to look at mobx-state-tree is to consider it, as argued by Daniel Earwicker, to be "React, but for data". Like React, MST consists of composable components, called models, which captures a small piece of state. They are instantiated from props (snapshots) and after that manage and protect their own internal state (using actions). Moreover, when apply snapshots, tree nodes are reconciled as much as possible. There is even a context-like mechanism, called environments, to pass information to deep descendants.

results matching ""

    No results matching ""