Unpeeling `observable` the onion

The most important change in MobX 3 is how observable data structures are created. The MobX 2 API has quite a few irregularities and edge cases. It was a community effort to redesign the observable API. In the longest thread in the issue tracker so far, many use cases and usage patterns were discussed. Many proposals were shot down in mid air, but I am very happy with the end result .

The new API follows the “onion pattern”. The API is now nicely layered; each API layer can be easily peeled back to reveal lower level functions. Let’s quickly walk through it.

The observable function / decorator works largely the same as in MobX 2. There are 3 notable changes:

Objects are by default no longer enhanced but cloned. This is consistent with arrays and maps, and paves the path to Proxies. ES6 Map support has been added (string based keys only). Argumentless function values are no longer automatically converted into computed properties. This should avoid a lot of confusion.

Feel free to skip the remainder of this section if you are not experienced with MobX; it’s quite detailed…

Observable

The observable(data) function constructs new observable collections. Object, maps, arrays or boxed observables. If we unpeel observable, you will find that it just calls one of the following methods: observable.object , observable.map , observable.array and observable.box . Feel free to use those methods directly instead of the generic observable method.

Collection factories

Each of these collection types have the same semantics: If you assign a non-observable, primitive value to them, MobX will automatically clone and convert that object into an observable. By calling observable(newValue) before storing that new value. This enables deep observability by default. This recursive process can be described as making data deep observable.

Shallow collection factories

However, sometimes you want a collection where MobX doesn’t try to enhance the data into an observable. For example when storing JSX or DOM elements, or objects that are managed by an external library. In other words, sometimes you just need a shallow collection. A collection that itself is observable, but the values it stores are not. For this purpose MobX now also exposes functions to create shallow collections: observable.shallowObject , observable.shallowMap , observable.shallowArray and observable.shallowBox . In addition, similar to extendObservable , there is now extendShallowObservable .

Decorators & modifiers

If you are using decorators, the @observable decorator applies the deep strategy. You can make this more explicit by actually using the @observable.deep decorator. Similarly there is the @observable.shallow decorator, which converts any value you assign to it into a shallow collection (only arrays and maps are supported). And finally there is @observable.ref , which leaves any value you assign to the property completely as is, and just creates an observable references to the value you assign to it (similar to observable.shallowBox )