We can watch MobX detect and modify mutations with the intercept
function and watches observable value changes with the observe
function.
In this article, we’ll look at how to use intercept
to monitor changes and detect and modify mutations before they’re applied to an observable and use observe
to watch for changes.
Intercept
We can call the intercept
function as follows:
intercept(target, propertyName?, interceptor)
In the signature above, target
is the observable to guard, propertyName
is an optional parameter to specify specific properties to intercept.
intercept(user.name, interceptor)
is different from intercept(user, "name", interceptor)
. The first tries to add an interceptor to the current value
inside user.name
and the latter intercept changes to the name
property of user
.
The interceptor
is a callback that’ll be run for each change that’s made to the observable.
The intercept
function tells MobX what needs to happen with the current change.
It should do one of the following things;
- Return the received
change
object as-is from the function - Modify the
change
object and return it - Return
null
to indicate the change shouldn’t be applied - Throw an exception
intercept
returns a disposer
function that can be used to cancel the interceptor when it’s invoked.
It’s possible to register multiple interceptors to the same observable. They’ll be chained in the registration order.
If one of the interceptors returns null
or throw an exception, then the other interceptors won’t be evaluated anymore.
It’s also possible to register an interceptor both on a parent object and an individual property.
For example, we can use it as follows:
import { observable, intercept } from "mobx";
const theme = observable({
color: "#ffffff"
});
const disposer = intercept(theme, "color", change => {
if (!change.newValue) {
return null;
}
if (change.newValue.length === 6) {
change.newValue = `#${change.newValue}`;
return change;
}
if (change.newValue.length === 7) {
return change;
}
if (change.newValue.length > 10) {
disposer();
}
throw new Error(`Invalid color`);
});
In the code above, we defined an interceptor for the color
property of the theme
observable object.
The interceptor callback takes a change
parameter with the value passed in. Then it runs through some checks and makes some changes to the newValue
property.
Observe
The observe
function is used to watch for changes of an observable.
It can be called as follows:
observe(target, propertyName?, listener, invokeImmediately?)
In the signature above, the target
is observable to watch.
propertyName
is an optional parameter to specify a property to observe. observe(user.name, listener)
is different from observe(user, "name", listener)
. The first observes the current value
in user.name
and the latter observes the name
property of user.
listener
is a callback that’ll be invoked for each change that’s made to the observable. It receives a single change object describing the mutation except for boxed observables, which will invoke a listener with the newValue
and oldValue
parameters.
invokeImmediately
is a boolean that’s false
by default. Set it to true
if we want observe
to invoke listener
directly with the state of the observable instead of waiting for the first change.
For example, we can use it as follows:
import { observable, observe } from "mobx";
const person = observable({
firstName: "John",
lastName: "Smith"
});
const disposer = observe(person, change => {
console.log(
`${change.type} ${change.name} from ${change.oldValue} to ${
change.object[change.name]
}`
);
});
person.firstName = "Jane";
In the code above, we have a callback with the change
object that has the type
of change, name
of the change, the oldValue
and the change.object[change.name]
to get the new value.
Events
The callbacks of intercept
and observe
will receive an event object which has the object
for the observable triggering the event and the type
string for the type of the current event.
They also have additional fields depending on the type:
Object add Event
add
event gives us the name
and newValue
properties for the name of the property being added and the new value being assigned respectively.
Object update Event
update
event gives us the name
and newValue
properties for the name of the property being added and the new value being assigned respectively.
In addition, it gives us the oldValue
property for the value that’s being replaced.
Array splice Event
This gives us index
for the starting index of the splice. Splices are also fired by other array methods like push
etc.
removedCount
gives us the amount of items being removed.
added
gives us an array of items being added.
removed
gives us an array with items being added.
addedCount
gives us the number of items that were added.
Array update Event
The update event gives us index
which has the index of the single entry that’s being updated
newValue
gives us the value that’s being assigned.
oldVale
has the old value that’s being replaced.
Map add and delete Events
The Map add and delete events has the name
and newValue
properties for the name of the property being added and the new value being assigned respectively.
Map update Event
The delete event gives us the properties that are sent with add and delete events and also has the oldValue
with the value that’s being replaced.
Boxed & computed observables create Events
The create event for boxed and computed observables has the newValue
property to get us the value that we assigned during creation.
Boxed & computed observables update Events
The update event also gives us the oldValue
with the value being replaced in addition to the newValue
.
Conclusion
We can use the intercept
function to do something before the mutation of the observable’s state is complete.
The observe
function can be used to watch for value changes in a MobX observable. It takes a callback that gives us old and new values of the state and other information.