Categories
Mobx

Watching MobX Observables with Intercept and Observe

Spread the love

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.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *