Intercept & Observe
observe
and intercept
can be used to monitor the changes of a single observable (they don't track nested observables).
intercept
can be used to detect and modify mutations before they are applied to the observable.
observe
allows you to intercept changes after they have been made.
Intercept
Usage: intercept(target, propertyName?, interceptor)
target
: the observable to guardpropertyName
: optional parameter to specify a specific property to intercept. Note thatintercept(user.name, interceptor)
is fundamentally different fromintercept(user, "name", interceptor)
. The first tries to add an interceptor to the currentvalue
insideuser.name
(which might not be an observable at all), the latter intercepts changes to thename
property ofuser
.interceptor
: callback that will be invoked for each change that is made to the observable. Receives a single change object describing the mutation.
The intercept
should tell MobX what needs to happen with the current change.
Therefore it should do one of the following things:
- Return the received
change
object as-is from the function, in wich case the mutation will be applied. - Modify the
change
object and return it, for example to normalize the data. Not all fields are modifiable, see below. - Return
null
, this indicates that the change can be ignored and shouldn't be applied. This is a powerful concept to make your objects for example temporarily immutable. - Throw an exception, for example if some invariant isn't met.
The function returns a disposer
function that can be used to cancel the interceptor.
It is possible to register multiple interceptors to the same observable.
They will be chained in registration order.
If one of the interceptors returns null
or throw an exception, the other interceptors won't be evaluated anymore.
It is also possible to register an interceptor both on a parent object and on an individual property.
In that case the parent object interceptors are run before the property interceptors.
const theme = observable({
backgroundColor: "#ffffff"
})
intercept(theme, "backgroundColor", change => {
if (!change.newValue) {
// ignore attempts to unset the background color
return null;
}
if (change.newValue.length === 6) {
// correct missing '#' prefix
change.newValue = '#' + change.newValue;
return change;
}
if (change.newValue.length === 7) {
// this must be a properly formatted color code!
return change;
}
throw new Error("This doesn't like a color at all: " + change.newValue);
})
Observe
Usage: observe(target, propertyName?, listener, invokeImmediately?)
target
: the observable to observepropertyName
: optional parameter to specify a specific property to observe. Note thatobserve(user.name, listener)
is fundamentally different fromobserve(user, "name", listener)
. The first observes the currentvalue
insideuser.name
(which might not be an observable at all), the latter observes thename
property ofuser
.listener
: callback that will be invoked for each change that is made to the observable. Receives a single change object describing the mutation, except for boxed observables, which will invoke thelistener
two parameters:newValue, oldValue
.invokeImmediately
: by default false. Set it to true if you wantobserve
to invokelistener
directly with the state of the observable (instead of waiting for the first change). Not supported (yet) by all kinds of observables.
The function returns a disposer
function that can be used to cancel the observer.
Note that transaction
does not affect the working of the observe
method(s).
This means that even inside a transaction observe
will fire its listeners for each mutation.
Hence autorun
is usually a more powerful and declarative alternative to observe
.
Example:
import {observable, observe} from 'mobx';
const person = observable({
firstName: "Maarten",
lastName: "Luther"
});
const disposer = observe(person, (change) => {
console.log(change.type, change.name, "from", change.oldValue, "to", change.object[change.name]);
});
person.firstName = "Martin";
// Prints: 'update firstName from Maarten to Martin'
disposer();
// Ignore any future updates
// observe a single field
const disposer2 = observe(person, "lastName", (newValue, oldValue) => {
console.log("LastName changed to ", newValue);
});
Related blog: Object.observe is dead. Long live mobx.observe
Event overview
The callbacks of intercept
and observe
will receive an event object which has at least the following properties:
object
: the observable triggering the eventtype
: (string) the type of the current event
These are the additional fields that are available per type:
observable type | event type | property | description | available during intercept | can be modified by intercept | |
---|---|---|---|---|---|---|
Object | add | name | name of the property being added | √ | ||
newValue | the new value being assigned | √ | √ | |||
update | name | name of the property being updated | √ | |||
newValue | the new value being assigned | √ | √ | |||
oldValue | the value that is replaced | |||||
Array | splice | index | starting index of the splice. Splices are also fired by push , unshift , replace etc. |
√ | ||
removedCount | amount of items being removed | √ | √ | |||
added | array with items being added | √ | √ | |||
removed | array with items that where removed | |||||
addCount | amount of items that where added | |||||
update | index | index of the single entry that is being updated | √ | |||
newValue | the newValue that is / will be assigned | √ | √ | |||
oldValue | the old value that was replaced | |||||
Map | add | name | the name of the entry that was added | √ | ||
newValue | the new value that is being assigned | √ | √ | |||
update | name | the name of the entry that is being updated | √ | |||
newValue | the new value that is being assigned | √ | √ | |||
oldValue | the value that has been replaced | |||||
delete | name | the name of the entry that is being removed | √ | |||
oldValue | the value of the entry that was removed | |||||
Boxed observable | create | newValue | the value that was assigned during creation. Only observable by spy |
|||
update | newValue | the new value being assigned (only available in intercept and spy ) |
√ | √ | ||
oldValue | .. the old value. Only available in spy |
|||||
(observe callback) | newValue | first param of the observe callback |
||||
(observe callback) | oldValue | second param of the observe callback previous value |
||||
Computed observable | (observe callback) | newValue | first param of the observe callback |
|||
(observe callback) | oldValue | second param of the observe callback |