The computed observable
The observables we have seen so far have a direct correspondence with the shape of the client-state. If you are representing a list of items, you would use an observable array in your client-state. Similarly, each item in a list can be an observable object or an observable map. The story does not stop there. MobX gives you yet another kind of observable, called a computed property or a computed observable.
A computed property is not an observable that is inherent to the client state. Instead, it is an observable that derives its value from other observables. Now, why would that be useful? you may ask. Let's take an example to see the benefits.
Consider the cart observable, which tracks a list of items. Take a look at this:
import { observable } from 'mobx';
const cart = observable.object({
items: [],
modified: new Date(),
});
Let's say you want to have a description property that describes the cart in this format: There {is, are} {no, one, n} item{s} in the cart.
For zero items, the description says this: There are no items in the cart.
When there is only one item, the description becomes this: There is one item in the cart.
For two or more items (n), the description should be: There are n items in the cart.
Let's ponder how we can go about modeling this property. Consider the following:
- Clearly, the description is not an inherent property of the cart. Its value depends on items.length.
- We can add an observable property called description, but then we have to update it anytime items or items.length changes. That is extra work and easy to forget. Also, we run the risk of someone modifying the description from the outside.
- Description should just be a getter with no setter. If someone is observing description, they should be notified anytime it changes.
As you can tell from the preceding analysis, we can't seem to fit this behavior into any of the previously-discussed observable types. What we need here is the computed property. We can define a computed description property by simply adding a get-property to the cart observable. It will derive its value from items.length. Take a look at this code block:
const cart = observable.object({
items: [],
modified: new Date(),
get description() {
switch (this.items.length) {
case 0:
return 'There are no items in the cart';
case 1:
return 'There is one item in the cart';
default:
return `There are ${this.items.length} items in the
cart`;
}
},
});
Now, you can simply read cart.description and always get the latest description. Anyone observing this property would be automatically notified when cart.description changes, which will happen if you add or remove items from the cart.The following is an example of how this computed property can be used:
cart.items.push({ name: 'Shoes', quantity: 1 });
console.log(cart.description);
Computed properties, also known as derivations, are one of the most powerful tools in the MobX toolbox. By thinking of your client-state in terms of a minimal set of observables and augmenting it with derivations (computed properties), you can model a variety of scenarios effortlessly. Computed properties derive their value from other observables. If any of these depending observables change, the computed property changes as well.
As you develop experience using MobX, you will realize that computed properties are possibly your best observable-friends.