Reactions

The MobX triad is completed when we add Reactions into the mix. Having reactions is what triggers the reactivity in the system. A reaction implicitly tracks all the observables which are being used and then re-executes its logic whenever the depending observables change.
Computed, a reaction?
Technically, a computed is also a reaction, aka Derivation, as it depends on other observables or computeds. The only difference between a regular
Reaction
andComputed
is that the former does not produce any value. Computeds are mostly read-only observables that derive their value from other observables.
Reactions, or Derivations come in few flavors: autorun
, reaction
, when
and of course the Flutter Widget: Observer
. All of these variations take a function that is tracked for any observables. When the tracked observables change, the function is re-executed. This simple behavior is the defining characteristic of a reaction. Note that there is no explicit subscription or wiring needed. Reactions also return a disposer-function (ReactionDisposer
) that can be invoked to pre-maturely dispose a reaction.
autorun
ReactionDisposer autorun(Function(Reaction) fn)
Runs the reaction immediately and also on any change in the observables used inside
fn
.
reaction
ReactionDisposer reaction<T>(T Function(Reaction) fn, void Function(T) effect)
Monitors the observables used inside the fn()
tracking function and runs the effect()
when
the tracking function returns a different value. Only the observables inside fn()
are tracked.
when
ReactionDisposer when(bool Function(Reaction) predicate, void Function() effect)
Monitors the observables used inside predicate()
and runs the effect()
when it returns true
. After the effect()
is run, when
automatically disposes itself. So you can think of when as a one-time reaction
. You can also dispose when()
pre-maturely.
asyncWhen
Future<void> asyncWhen(bool Function(Reaction) predicate)
Similar to when
but returns a Future
, which is fulfilled when the predicate()
returns true. This is a convenient way of waiting for the predicate()
to turn true
.
Observer Widget
Observer({@required Widget Function(BuildContext context) builder})
One of the most visual reactions in the app is the UI. The Observer widget (which is part of the
package), provides a granular observer of the observables used in its
builder
function. Whenever these observables change, Observer
rebuilds and renders.
Immediate context for
builder
The key thing to note is that the
builder
function will only track the observables in its immediate execution context. If an observable is being used in a nested function, it will not be tracked. Make sure to dereference (a.k.a. read) an observable in the immediate execution context. If no observables are found when running thebuilder
function, it will warn you on the console.This is one of the most common gotchas when using MobX. Just because you have nested functions inside a
builder
, which are reading an observable, it does not actually track the observable. It can appear deceiving but the fact is, the observable was not in the immediate execution context. Be watchful for this scenario, especially when yourObserver
-wrappedWidgets
are not updating properly.
Below is the Counter example in its entirety.