Skip to main content

When does MobX react?

MobX is a reactive system that waits for an observable to change. Every observable has the basic capability of notifying whenever its value changes. Interestingly, this is only half the story in the world of MobX. To make the system truly reactive, there needs to be two sides to this reactive-equation.

This is the classic case of Producer → Consumer relationship. On one side is the observables, that notifies of changes and on the consumer side are reactions that "react" to these changes. Without the reactions, MobX would be a a very quiet place with zero activity (reactivity?).

Now the important question is: When does MobX react?

Rules of Reactivity

At the heart of MobX are observables. The reactivity occurs when there is a reaction that depends on an observable. When the observables change, the associated reactions will re-trigger automatically. Yes, automatically, without any manual wiring. The way you link a reaction to an observable is by using it (reading its value) inside the tracking function.

Since there are 3 forms of reactions: autorun, reaction, when, the tracking functions are slightly different.

Thus, following are the things to keep in mind, when using MobX:

1. Notifications fire when an observable changes value

Every observable tracks a value. When you assign a new value to the observable, it will notify all of its associated reactions. If there are no reactions (aka observers), you can tell that nothing will happen. The notification will be lost in the deep, dark abyss of software. Ok, digression alert!.

The value an observable holds, can be anything from a simple number to a complex data structure like a List, Map, Set, etc. When you mark a field with the @observable annotation, you are essentially creating an Observable<T>, whose value is of the type you specified for the field.

part 'person.g.dart';

class Person = _Person with _$Person;

abstract class _Person with Store {
@observable
String name;

@observable
String email;
}

In the above code, you have two observables of type Observable<String>. When a new value is assigned to these fields, the notifications will fire and all the associated reactions will be re-triggered.

final person = Person()..name = 'MobX';

reaction((_) => person.name, (name) => print(name));

person.name = 'Pavan Podila'; // !!! notifications fired, reactions triggered !!!

2. Tracking is automatic when a read happens

In the above code, notice that there is no explicit subscription or wiring needed to connect the reaction to the observable. The simple act of reading a value is enough. The fact that you are reading the person.name in the tracker function (the first argument) is enough for MobX to do its work. Behind the scenes it will link the reaction to the the person.name observable and start tracking, automatically.

3. Beware of reading values instead of Observables

This is just an extension of Rule 1. For MobX to do its job, you have to ensure only Observables are being read inside the reaction. If you happen to read the value instead of the observable, no tracking will take place. A simple example is shown below:

final count = Observable(10);
var value = count.value;

reaction((_) => value, (v) => print('New value: $v'));

Notice that we are reading the value inside the reaction's tracker-function. This is not an observable, but just a plain number. MobX will not do any tracking and effectively this reaction will never re-execute again!.

List<T> vs ObservableList<T>

The List<T> interface from Dart has no notion of observability. It is inert from the perspective of MobX. When you add an item to the list, remove an item or modify an item at some index, List<T> will not notify MobX. To get the reactive behavior, you have to use the ObservableList<T>. Now when you add, remove, modify items, MobX will know about the changes and notify all associated reactions.

By the same token, use the ObservableMap<K, V> or ObservableSet<T> when you want to MobX to track changes to these data structures.

Similarly, ObservableFuture<T> and ObservableStream<T> are wrappers around the standard Future<T> and Stream<T>, which will give you the reactive behavior for futures and streams.