MobX has a set of decorators to change how observable properties will behave.
In this article, we’ll look at them one by one and see how we can use them.
Modifiers
MobX comes with the following decorators that defines how observable properties will behave:
observable
— an alias forobservable.deep
observable.deep
— the default modifier used by any observable. It clones and converts any array, map or plain object into its observable counterpartobservable.ref
— disables automatic observable conversion and creates an observable reference insteadobservable.shallow
— can only be used with collections. Turns any assigned collection into an observable, but the values will be treated as-isobservable.struct
— likeref
but ignore new values that are structurally equal to the current valuecomputed
— creates a derived propertycomputed(options)
— same ascomputed
, but sets additional optionscomputed.struct
— same ascomputed
, but only notify any of is observers when the value produced is structurally different from the previous value.action
— creates an actionaction(name)
— creates action and overrides the nameaction.bound
— creates action and bindsthis
to the instance.
Decorators can be used the MobX’s decorate
, observable.object
, extendObservable
and observable
to specify how object members should behave.
observable.deep
is the default behavior for any key-value pair by default and computed
for getters.
For example, we can define an observable as follows:
import { observable, action } from "mobx";
const person = observable(
{
firstName: "John",
lastName: "Smith",
age: 42,
get fullName() {
return `${this.firstName} ${this.lastName}`;
},
setAge(age) {
this.age = age;
}
},
{
setAge: action
}
);
In the code above, we have the default decorators for all members except for setAge
, which we explicitly defined as an action
.
Therefore, firstName
, lastName
, and age
are observable
s and fullName
is a computed
field.
We can use the decorate
function as follows:
import { observable, action, decorate } from "mobx";
class Person {
firstName = "John";
lastName = "Smith";
age = 42;
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
setAge(age) {
this.age = age;
}
}
decorate(Person, {
firstName: observable,
lastName: observable,
age: observable,
setAge: action
});
In the code above, we call decorate
with the class as the first argument, and then an object with the fields we want to modify as the second argument.
We set firstName
, lastName
and age
as observables and setAge
as an action.
fullName
is a computed field since it’s the default option for getters.
Deep Observability
When MobX creates an observable object using observable
, observable.object
or extendObservable
, it introduces observable properties which use the deep
modifier by default.
The deep modifier recursively calls observable(newValue)
for any assigned value which uses the deep
modifier until it gets to the bottom level of the object.
Reference Observability
In some cases, objects don’t need to be converted into observables. For example, we don’t want to do this for immutable objects or objects that are managed by an external library.
In this case, we can use the ref
modifier.
For example, we can use it as follows:
class Person {
firstName = "John";
lastName = "Smith";
@observable.ref age = 42;
}
In the code above, we added the observable.ref
decorator to age
so that MobX will only track its reference but doesn’t try to convert its value.
With ES5 syntax, we can write the following:
import { observable, extendObservable } from "mobx";
function Person() {
extendObservable(
this,
{
name: "Joe",
age: 42
},
{
age: observable.ref
}
);
}
Shallow Observability
We can use the observable.shallow
modifier to apply observability one level deep. We need this to create a collection of observable references.
It won’t recursively apply observability like deep
.
For example, we can use it as follows:
class Books {
@observable.shallow authors = [];
}
{ deep: false }
can be passed as an option to observable
, observable.object
, observable.array
, observable.map
, and extendObservable
to create shallow collections.
Conclusion
We can use modifiers to change the way MobX watches the changes in the values.
The default is that it looks at a value recursively for changes.
There’re also modifiers for computed values, shallow watch, and more.