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 {
String name;
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.