Skip to main content

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 and Computed 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.

import 'package:mobx/mobx.dart';

final greeting = Observable('Hello World');

final dispose = autorun((_){
print(greeting.value);
});

greeting.value = 'Hello MobX';

// Done with the autorun()
dispose();


// Prints:
// Hello World
// Hello MobX

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.

import 'package:mobx/mobx.dart';

final greeting = Observable('Hello World');

final dispose = reaction((_) => greeting.value, (msg) => print(msg));

greeting.value = 'Hello MobX'; // Cause a change

// Done with the reaction()
dispose();


// Prints:
// Hello MobX

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.

import 'package:mobx/mobx.dart';

final greeting = Observable('Hello World');

final dispose = when((_) => greeting.value == 'Hello MobX', () => print('Someone greeted MobX'));

greeting.value = 'Hello MobX'; // Causes a change, runs effect and disposes


// Prints:
// Someone greeted MobX

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.

final completed = Observable(false);

void waitForCompletion() async {
await asyncWhen(() => completed.value == true);

print('Completed');
}