It's time to take action
Although we are mutating cart.itemCount directly, it is definitely not the recommended approach. Remember that the state should not be changed directly and instead should be done via actions. The use of an action also adds vocabulary to the operations that can be performed on the observable state.
In our case, we can call the state mutation that we were doing as an incrementCount action. Let's use the MobX action API to encapsulate the mutation:
import { observable, autorun, action } from 'mobx';
let cart = observable({
itemCount: 0,
modified: new Date(),
});
autorun(() => {
console.log(`The Cart contains ${cart.itemCount} item(s).`);
});
const incrementCount = action(() => {
cart.itemCount++;
});
incrementCount();
The action API takes a function that will be called whenever the action is invoked. It may seem superfluous that we are passing a function into action, when we could just wrap the mutation inside a plain function and call the plain function instead. An astute thought, for sure. Well, there is a good reason for that. Internally, action is doing much more than being a simple wrapper. It ensures that all notifications for state changes are fired, but only after the completion of the action function.
When you are modifying a lot of observables inside your action, you don't want to be notified about every little change immediately. Instead, you want to be able to wait for all changes to complete and then fire the notifications. This makes the system more performant and also reduces the noise of too many notifications, too soon.
Going back to our example, we can see that wrapping it in an action also improves the readability of the code. By giving a specific name to the action (incrementCount) we have added vocabulary to our domain. In doing so, we can abstract the details of what is needed to actually increment the count.
Observables, observers, and actions are at the core of MobX. With these fundamental concepts, we can build some of the most powerful and complex React applications.
Note the striking similarity with the uni-directional data flow that we saw earlier. Observables capture the state of your application. Observers (also called reactions) include both the side effect handlers as well as the UI. The actions are, well, actions that cause a change in the observable state: