简介:
概念
MobX 区分了以下几个应用中的概念:
State(状态)
状态 是驱动应用的数据。 通常有像待办事项列表这样的领域特定状态,还有像当前已选元素的视图状态。 记住,状态就像是有数据的excel表格。
Derivations(衍生)
任何 源自状态并且不会再有任何进一步的相互作用的东西就是衍生。 衍生以多种形式存在:
- 用户界面
- 衍生数据,比如剩下的待办事项的数量。
- 后端集成,比如把变化发送到服务器端。
MobX 区分了两种类型的衍生:
- Computed values(计算值) - 它们是永远可以使用纯函数(pure function)从当前可观察状态中衍生出的值。
- Reactions(反应) - Reactions 是当状态改变时需要自动发生的副作用。需要有一个桥梁来连接命令式编程(imperative programming)和响应式编程(reactive programming)。或者说得更明确一些,它们最终都需要实现I / O 操作。
刚开始使用 MobX 时,人们倾向于频繁的使用 reactions。 黄金法则: 如果你想创建一个基于当前状态的值时,请使用 computed
。
回到excel表格这个比喻中来,公式是计算值的衍生。但对于用户来说,能看到屏幕给出的反应则需要部分重绘GUI。
Actions(动作)
动作 是任一一段可以改变状态的代码。用户事件、后端数据推送、预定事件、等等。 动作类似于用户在excel单元格中输入一个新的值。
在 MobX 中可以显式地定义动作,它可以帮你把代码组织的更清晰。 如果是在严格模式下使用 MobX的话,MobX 会强制只有在动作之中才可以修改状态。
原则
MobX 支持单向数据流,也就是动作改变状态,而状态的改变会更新所有受影响的视图。
当状态改变时,所有衍生都会进行原子级的自动更新。因此永远不可能观察到中间值。
所有衍生默认都是同步更新。这意味着例如动作可以在改变状态之后直接可以安全地检查计算值。
计算值 是延迟更新的。任何不在使用状态的计算值将不会更新,直到需要它进行副作用(I / O)操作时。 如果视图不再使用,那么它会自动被垃圾回收。
所有的计算值都应该是纯净的。它们不应该用来改变状态。
相关文章:
mobx官方文档:
https://cn.mobx.js org/refguide/action.html
https://mobx.netlify.app/api/reaction
原理:
https://takeroro.github.io/2020/06/30/mobX%20flutter%20%E6%95%B0%E6%8D%AE%E6%B5%81%E5%8A%A8/
https://juejin.cn/post/6844903860184563720
https://www.jianshu.com/p/a47d77f6371d
https://zhuanlan.zhihu.com/p/421675450
基本用法:
-
使用
Observer
包裹需要刷新的UI组件。 -
创建可观察的
model
-
使用命令自动生成.g文件
flutter pub run build_runner build
示例代码:
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'counter.dart';
class CounterExample extends StatefulWidget {
const CounterExample({Key? key}) : super(key: key);
CounterExampleState createState() => CounterExampleState();
}
class CounterExampleState extends State<CounterExample> {
final Counter counter = Counter();
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
title: const Text('MobX Counter'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Observer(
builder: (_) => Text(
'${counter.value}',
style: const TextStyle(fontSize: 40),
)),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: counter.increment,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'counter.dart';
// **************************************************************************
// StoreGenerator
// **************************************************************************
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
mixin _$Counter on _Counter, Store {
final _$valueAtom = Atom(name: '_Counter.value');
int get value {
_$valueAtom.reportRead();
return super.value;
}
set value(int value) {
_$valueAtom.reportWrite(value, super.value, () {
super.value = value;
});
}
final _$_CounterActionController = ActionController(name: '_Counter');
void increment() {
final _$actionInfo =
_$_CounterActionController.startAction(name: '_Counter.increment');
try {
return super.increment();
} finally {
_$_CounterActionController.endAction(_$actionInfo);
}
}
String toString() {
return '''
value: ${value}
''';
}
}
import 'package:mobx/mobx.dart';
part 'counter.g.dart';
class Counter = _Counter with _$Counter;
abstract class _Counter with Store {
int value = 0;
void increment() {
value++;
}
}
依赖:
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
mobx: ^2.0.6+1
flutter_mobx: ^2.0.4
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.1.0
mobx_codegen: ^2.0.0
运行原理:
Observer
组件初始化
首先父组件调用build
,接着创建Observer
组件,调用Observer
组件的mount
和build
Observer
继承关系
Observer
组件继承自StatelessObservderWidget
class Observer extends StatelessObserverWidget
StatelessObserverWidget
调用了createElement
方法
abstract class StatelessObserverWidget extends StatelessWidget
with ObserverWidgetMixin {
...
StatelessObserverElement createElement() => StatelessObserverElement(this);
}
StatelessObserverElement
混入了ObserverElementMixin
类
class StatelessObserverElement extends StatelessElement
with ObserverElementMixin {
ObserverElementMixin
继承自ComponentElement
,ComponentElement
继承自Element
mount
方法
ObserverElementMixin
复写了Element
类的mount
方法
element
的mount
方法的作用:将该元素添加到指定父节点的指定槽位的树中。当一个新创建的元素被添加到框架中时,框架调用这个函数
mount
方法,在初始化的时候被调用。
mixin ObserverElementMixin on ComponentElement {
...
late ReactionImpl _reaction;
...
void mount(Element? parent, dynamic newSlot) {
_reaction = _widget.createReaction(invalidate, onError: (e, _) {
···
}) as ReactionImpl;
super.mount(parent, newSlot);
}
void invalidate() => markNeedsBuild();
...
}
ObserverElementMixin
调用了_widget.createReaction
方法,用于_reaction
初始化:
mixin ObserverWidgetMixin on Widget {
...
ReactiveContext getContext() => mainContext;
/// A convenience method used for testing.
Reaction createReaction(
Function() onInvalidate, {
Function(Object, Reaction)? onError,
}) =>
ReactionImpl(
getContext(),
onInvalidate,
name: getName(),
onError: onError,
);
...
}
_reaction
初始化时传入的invalidate
如下,该方法是状态变换时widget
的行为,就是重新绘制
void invalidate() => markNeedsBuild();
ReactionImpl
类
每个ObserverElementMixin
都持有一个ReactionImpl
类型的_reaction
,
可以这么理解,_reaction
等同于一个Observer
组件
ReactionImpl
类的继承关系如下:
Derivation
类的代码如下:
abstract class Derivation {
String get name;
late Set<Atom> _observables;
Set<Atom>? _newObservables;
MobXCaughtException? _errorValue;
MobXCaughtException? get errorValue;
late DerivationState _dependenciesState;
void _onBecomeStale();
// ignore: unused_element
void _suspend();
}
每个derivation
类都有自己的name
,_observables
监听者集合, _dependenciesState
状态
我们来简单看下mobx里的几种状态(DerivationState
)
enum DerivationState {
// 在运行之前或(在批处理之外并且没有被观察到)此时派生没有保存任何关于依赖树的数据
notTracking,
// 自上次计算以来没有改变浅依赖不会重新计算推导这就是使 mobx 快速的原因
upToDate,
// 一些深度依赖改变了,但是不知道浅依赖改变是否需要先检查 UP_TO_DATE 或 POSSIBLY_STALE 目前只有 Computed 会传播 POSSIBLY_STALE
possiblyStaleß,
// 自上次计算以来,浅层依赖关系发生了变化,并且在下次需要时需要重新计算推导。
stale
}
ReactiveContext
类
MobX 的主要Context
。 所有反应性操作和观察都在发生在这个Context
中。 单例。
主要的属性和方法如下:
class ReactiveContext {
ReactiveContext({ReactiveConfig? config}) {
this.config = config ?? ReactiveConfig.main;
}
late ReactiveConfig _config;
_ReactiveState _state = _ReactiveState();
void startBatch() {
···
}
void endBatch() {
···
}
Derivation? _startTracking(Derivation derivation) {
···
}
void _endTracking(Derivation currentDerivation, Derivation? prevDerivation) {
···
}
T? trackDerivation<T>(Derivation d, T Function() fn) {
···
}
void _reportObserved(Atom atom) {
···
}
void _bindDependencies(Derivation derivation) {
···
}
void runReactions() {
···
}
void _runReactionsInternal() {
···
}
}
_ReactiveState
类
简单介绍一下context
类里的state
class _ReactiveState {
/// 当前批次深度。 这用于跟踪 `transaction` / `action` 的深度。
/// 当批处理结束时,我们执行所有的 [pendingReactions]
int batch = 0;
/// 跟踪当前执行的Derivation(reactions or computeds)。
/// 这里使用的 Observables 链接到这个Derivation。
Derivation? trackingDerivation;
/// Are we in middle of executing the [pendingReactions].
bool isRunningReactions = false;
···
}
build
方法:
初始化时build调用了两次,调用super.build调用一次,接着自身又调用一次
在mount
方法执行完成之后,ObserverElementMixin
重写的build
方法被调用。
mixin ObserverElementMixin on ComponentElement {
···
Widget build() {
late final Widget built;
reaction.track(() {
built = super.build();
});
···
return built;
}
···
}
ObserverElementMixin
的build
方法,调用了ReactionImpl
的track
方法
ReactionImpl
类里的track
方法主要调用了ReactiveContext
里的三个方法
void track(void Function() fn) {
_context.startBatch();
···
_context.trackDerivation(this, fn);
···
_context.endBatch();
}
startBatch
方法:
void startBatch() {
_state.batch++;
}
trackDerivation
方法:
trackDerivation
方法有两个参数,一个是ReactionImpl
对象的内存地址,一个是Widget
的build
函数。
T? trackDerivation<T>(Derivation d, T Function() fn) {
final prevDerivation = _startTracking(d);
T? result;
···
result = fn();
···
_endTracking(d, prevDerivation);
return result;
}
trackDerivation
方法内部主要调用了两个方法_startTracking
和 _endTracking
-
_startTracking
方法:_startTracking
将当前ReactiveContext
持有的_ReactivState
里的trackingDerivation
设置为当前ReactionImpl
ReactiveContext
持有的_ReactivState
里的trackingDerivation
个人理解就是记录当前执行任务的ReactionImpl
首先
_startTracking
调用_resetDerivationState
Derivation? _startTracking(Derivation derivation) { final prevDerivation = _state.trackingDerivation; _state.trackingDerivation = derivation; _resetDerivationState(derivation); derivation._newObservables = {}; return prevDerivation; }
在
_resetDerivationState
方法里更改了状态,将当前ReactionImpl
的_dependenciesState
状态变为upToDate
,将当前ReactionImpl
的_observables
里的``Atom的
_lowestObserverState状态变为
upToDate`void _resetDerivationState(Derivation d) { if (d._dependenciesState == DerivationState.upToDate) { return; } d._dependenciesState = DerivationState.upToDate; for (final obs in d._observables) { obs._lowestObserverState = DerivationState.upToDate; } }
-
result = fn()
:result = fn()
调用了fn()
方法() { built = super.build(); }
调用了父组件的
build
,给Observer
组件的built
方法赋值父组件
build
后就会调用CounterExampleState
组件的build
,而在CounterExampleState
组件里使用了CounterBase
的值,所以在这里就会走到CounterBase
下的get value
方法里get value { _$valueAtom.reportRead(); return super.value; }
int他将
atom
添加到ReactionImpl
维护的_newObservables
里主要看一下第一个方法
_reportObserved
。void _reportObserved(Atom atom) { final derivation = _state.trackingDerivation; if (derivation != null) { derivation._newObservables!.add(atom); } }
可以这样理解,当前的
element
一旦使用了atom
维护的value
(会调用_$Counter.getValue
),这一步就会把对应的atom
加入到element
下的ReactionImpl
维护的_newObservables
里接着回到
_context.trackDerivation
方法里,fn
执行完成之后,执行_endTracking
-
_endTracking
方法:_endTracking
将当前ReactiveContext
持有的_ReactivState
里的trackingDerivation
设置为prevDerivation
void _endTracking(Derivation currentDerivation, Derivation? prevDerivation) { _state.trackingDerivation = prevDerivation; _bindDependencies(currentDerivation); }
_bindDependencies
代码如下,ReactionImpl
类维护了两个_observables
集合将当前
ReactionImpl
加入到Atom
类型的observable
集合的每一个子元素Atom
中,只保留活跃的Atom
,删除旧的Atom
void _bindDependencies(Derivation derivation) { final staleObservables = derivation._observables.difference(derivation._newObservables!); final newObservables = derivation._newObservables!.difference(derivation._observables); var lowestNewDerivationState = DerivationState.upToDate; // Add newly found observables for (final observable in newObservables) { observable._addObserver(derivation); ··· } // Remove previous observables for (final ob in staleObservables) { ob._removeObserver(derivation); } ··· derivation .._observables = derivation._newObservables! .._newObservables = {}; // No need for newObservables beyond this point }
endBatch
方法:
endBatch
会判断context
的state
是否还有任务,当没有任务的时候,调用runReactions
。
void endBatch() {
if (--_state.batch == 0) {
runReactions();
···
}
如果还有待办batch
,或者已经正在执行重绘就返回
void runReactions() {
if (_state.batch > 0 || _state.isRunningReactions) {
return;
}
_runReactionsInternal();
}
void _runReactionsInternal() {
...
//context里_state的待办Reactions集合,也就是需要刷新的Observer
final allReactions = _state.pendingReactions;
final remainingReactions = allReactions.toList(growable: false);
//清空待办
allReactions.clear();
for (final reaction in remainingReactions) {
//
reaction._run();
}
//清空待办
_state
..pendingReactions = []
..isRunningReactions = false;
}
run
里主要执行_onInvalidate()
void _run() {
_context.startBatch();
_onInvalidate();
_context.endBatch();
}
还记得之前在ReactionImpl
类初始化时介绍的吗
_reaction
初始化时传入的invalidate
如下,该方法是状态变换时widget
的行为,就是重新绘制
void invalidate() => markNeedsBuild();
完整的更改value过程:
在阅读完整过程之前,先简单的介绍一下action
part of '../core.dart';
class Action {
/// Creates an action that encapsulates all the mutations happening on the
/// observables.
///
/// Wrapping mutations inside an action ensures the depending observers
/// are only notified when the action completes. This is useful to silent the notifications
/// when several observables are being changed together. You will want to run your
/// reactions only when all the mutations complete. This also helps in keeping
/// the state of your application consistent.
///
/// You can give a debug-friendly [name] to identify the action.
///
/// ```
/// var x = Observable(10);
/// var y = Observable(20);
/// var total = Observable(0);
///
/// autorun((){
/// print('x = ${x}, y = ${y}, total = ${total}');
/// });
///
/// var totalUp = Action((){
/// x.value++;
/// y.value++;
///
/// total.value = x.value + y.value;
/// }, name: 'adder');
/// ```
/// Even though we are changing 3 observables (`x`, `y` and `total`), the [autorun()]
/// is only executed once. This is the benefit of action. It batches up all the change
/// notifications and propagates them only after the completion of the action. Actions
/// can also be nested inside, in which case the change notification will propagate when
/// the top-level action completes.
factory Action(Function fn, {ReactiveContext? context, String? name}) =>
Action._(context ?? mainContext, fn, name: name);
Action._(ReactiveContext context, this._fn, {String? name})
: _controller = ActionController(context: context, name: name);
String get name => _controller.name;
final ActionController _controller;
final Function _fn;
dynamic call([List args = const [], Map<String, dynamic>? namedArgs]) {
final runInfo = _controller.startAction();
try {
// Invoke the actual function
if (namedArgs == null) {
return Function.apply(_fn, args);
} else {
// Convert to symbol-based named-args
final namedSymbolArgs =
namedArgs.map((key, value) => MapEntry(Symbol(key), value));
return Function.apply(_fn, args, namedSymbolArgs);
}
} finally {
_controller.endAction(runInfo);
}
}
}
/// `ActionController` is used to define the start/end boundaries of code which
/// should be wrapped inside an action. This ensures all observable mutations are neatly
/// encapsulated.
///
/// You would rarely need to use this directly. This is primarily meant for the **`mobx_codegen`** package.
///
class ActionController {
ActionController({ReactiveContext? context, String? name})
: this._(context ?? mainContext, name: name);
ActionController._(this._context, {String? name})
: name = name ?? _context.nameFor('Action');
final ReactiveContext _context;
final String name;
ActionRunInfo startAction({String? name}) {
final reportingName = name ?? this.name;
_context.spyReport(ActionSpyEvent(name: reportingName));
final startTime = _context.isSpyEnabled ? DateTime.now() : null;
final prevDerivation = _context.startUntracked();
_context.startBatch();
final prevAllowStateChanges = _context.startAllowStateChanges(allow: true);
return ActionRunInfo(
prevDerivation: prevDerivation,
prevAllowStateChanges: prevAllowStateChanges,
name: reportingName,
startTime: startTime,
);
}
void endAction(ActionRunInfo info) {
final duration = _context.isSpyEnabled
? DateTime.now().difference(info.startTime!)
: Duration.zero;
_context.spyReport(
EndedSpyEvent(type: 'action', name: info.name, duration: duration),
);
// ignore: cascade_invocations
_context
..endAllowStateChanges(allow: info.prevAllowStateChanges)
..endBatch()
..endUntracked(info.prevDerivation);
}
}
class ActionRunInfo {
ActionRunInfo({
required this.name,
this.startTime,
this.prevDerivation,
this.prevAllowStateChanges = true,
});
final Derivation? prevDerivation;
final bool prevAllowStateChanges;
final String name;
final DateTime? startTime;
}
action,创建一个封装所有发生在可观察对象上的变化的action。在action中包装变化可确保仅在action完成时通知依赖的观察者。当多个可观察对象一起更改时,这对于使通知静音很有用。只有在所有变化完成后,您才会想要运行您的反应。这也有助于保持应用程序状态的一致性。
首先是点击事件increment()
,可以看到开启了一个action
void increment() {
final _$actionInfo = _$CounterBaseActionController.startAction(
name: 'CounterBase.increment');
try {
return super.increment();
} finally {
_$CounterBaseActionController.endAction(_$actionInfo);
}
}
startAction()
主要调用了_context.startBatch();
ActionRunInfo startAction({String? name}) {
final reportingName = name ?? this.name;
_context.spyReport(ActionSpyEvent(name: reportingName));
final startTime = _context.isSpyEnabled ? DateTime.now() : null;
final prevDerivation = _context.startUntracked();
_context.startBatch();
final prevAllowStateChanges = _context.startAllowStateChanges(allow: true);
return ActionRunInfo(
prevDerivation: prevDerivation,
prevAllowStateChanges: prevAllowStateChanges,
name: reportingName,
startTime: startTime,
);
}
ReactiveContext
下的startBatch
,该state
为Context
持有的_ReactiveState
void startBatch() {
_state.batch++;
}
接下来调用set value
方法
set value(int value) {
_$valueAtom.reportWrite(value, super.value, () {
super.value = value;
});
}
存值的时候调用了reportWrite
方法
void reportWrite<T>(T newValue, T oldValuße, void Function() setNewValue) {
context.spyReport(ObservableValueSpyEvent(this,
newValue: newValue, oldValue: oldValue, name: name));
final actionName = context.isSpyEnabled ? '${name}_set' : name;
// ignore: cascade_invocations
context.conditionallyRunInAction(() {
setNewValue();
reportChanged();
}, this, name: actionName);
// ignore: cascade_invocations
context.spyReport(EndedSpyEvent(type: 'observable', name: name));
}
conditionallyRunInAction
方法在第一次运行的时候isWithinBatch
是true
,没有开启新的action
而是直接开始执行fn()
void conditionallyRunInAction(void Function() fn, Atom atom,
{String? name, ActionController? actionController}) {
if (isWithinBatch) {
enforceWritePolicy(atom);
fn();
} else {
final controller = actionController ??
ActionController(
context: this, name: name ?? nameFor('conditionallyRunInAction'));
final runInfo = controller.startAction();
try {
enforceWritePolicy(atom);
fn();
} finally {
controller.endAction(runInfo);
}
}
}
执行fn()
也就是 setNewValue()
和reportChanged()
,调用reportChanged
void reportChanged() {
_context
..startBatch()
..propagateChanged(this)
..endBatch();
}
首先是ReactiveContext
下的startBatch
,该state
为Context
持有的_ReactiveState
void startBatch() {
_state.batch++;
}
接着调用propagateChanged
方法,将所有订阅该atom
的element
下的ReactionImpl
的状态都变为需要更新。
void propagateChanged(Atom atom) {
if (atom._lowestObserverState == DerivationState.stale) {
return;
}
atom._lowestObserverState = DerivationState.stale;
for (final observer in atom._observers) {
if (observer._dependenciesState == DerivationState.upToDate) {
observer._onBecomeStale();
}
observer._dependenciesState = DerivationState.stale;
}
}
当observer._dependenciesState == DerivationState.upToDate
为真时, 执行 onBecomeStale
void _onBecomeStale() {
schedule();
}
void schedule() {
if (_isScheduled) {
return;
}
_isScheduled = true;
_context
..addPendingReaction(this)
..runReactions();
}
在第一次调用时,batch
的数量大于0,因为上面开启了一次action
,调用了startBatch
,并且reportChanged
调用了startBatch
void runReactions() {
if (_state.batch > 0 || _state.isRunningReactions) {
return;
}
_runReactionsInternal();
}
然后来到endBatch()
,endBatch首先将_state.batch
进行--
操作,代表着执行完了一个batch
,只有所有batch
都执行完成时,才会运行runReactions
。
先简单看下_ReactiveState
的batch
class _ReactiveState {
/// 当前批次深度。 这用于跟踪 `transaction` / `action` 的深度。
/// 当批处理结束时,我们执行所有的 [pendingReactions]
int batch = 0;
void endBatch() {
if (--_state.batch == 0) {
runReactions();
for (var i = 0; i < _state.pendingUnobservations.length; i++) {
final ob = _state.pendingUnobservations[i]
.._isPendingUnobservation = false;
if (ob._observers.isEmpty) {
if (ob._isBeingObserved) {
// if this observable had reactive observers, trigger the hooks
ob
.._isBeingObserved = false
.._notifyOnBecomeUnobserved();
}
if (ob is Computed) {
ob._suspend();
}
}
}
_state.pendingUnobservations = [];
}
}
运行到这里内部的batch
已经执行完了,接下来会执行外部action
的endAction
,再次贴一下代码
void increment() {
final _$actionInfo = _$CounterBaseActionController.startAction(
name: 'CounterBase.increment');
try {
return super.increment();
} finally {
_$CounterBaseActionController.endAction(_$actionInfo);//这里被调用了
}
}
endAction
代码如下
void endAction(ActionRunInfo info) {
final duration = _context.isSpyEnabled
? DateTime.now().difference(info.startTime!)
: Duration.zero;
_context.spyReport(
EndedSpyEvent(type: 'action', name: info.name, duration: duration),
);
// ignore: cascade_invocations
_context
..endAllowStateChanges(allow: info.prevAllowStateChanges)
..endBatch()
..endUntracked(info.prevDerivation);
}
再次进入endBatch()
void endBatch() {
if (--_state.batch == 0) {
runReactions();
for (var i = 0; i < _state.pendingUnobservations.length; i++) {
final ob = _state.pendingUnobservations[i]
.._isPendingUnobservation = false;
if (ob._observers.isEmpty) {
if (ob._isBeingObserved) {
// if this observable had reactive observers, trigger the hooks
ob
.._isBeingObserved = false
.._notifyOnBecomeUnobserved();
}
if (ob is Computed) {
ob._suspend();
}
}
}
_state.pendingUnobservations = [];
}
}
这次所有的batch
执行完了,batch
已经为0
void runReactions() {
if (_state.batch > 0 || _state.isRunningReactions) {
return;
}
_runReactionsInternal();
}
_runReactionsInternal
主要执行 reaction. _run();
void _runReactionsInternal() {
_state.isRunningReactions = true;
var iterations = 0;
final allReactions = _state.pendingReactions;
// While running reactions, new reactions might be triggered.
// Hence we work with two variables and check whether
// we converge to no remaining reactions after a while.
while (allReactions.isNotEmpty) {
if (++iterations == config.maxIterations) {
final failingReaction = allReactions[0];
// Resetting ensures we have no bad-state left
_resetState();
throw MobXCyclicReactionException(
"Reaction doesn't converge to a stable state after ${config.maxIterations} iterations. Probably there is a cycle in the reactive function: $failingReaction");
}
final remainingReactions = allReactions.toList(growable: false);
allReactions.clear();
for (final reaction in remainingReactions) {
reaction._run();
}
}
_state
..pendingReactions = []
..isRunningReactions = false;
}
run
里主要执行_onInvalidate()
void _run() {
if (_isDisposed) {
return;
}
_context.startBatch();
_isScheduled = false;
if (_context._shouldCompute(this)) {
try {
_onInvalidate();
} on Object catch (e, s) {
// Note: "on Object" accounts for both Error and Exception
_errorValue = MobXCaughtException(e, stackTrace: s);
_reportException(_errorValue!);
}
}
_context.endBatch();
}
创建Atom
完整代码:
class Atom {
/// Creates a simple Atom for tracking its usage in a reactive context. This is useful when
/// you don't need the value but instead a way of knowing when it becomes active and inactive
/// in a reaction.
///
/// Use the [onObserved] and [onUnobserved] handlers to know when the atom is active and inactive
/// respectively. Use a debug [name] to identify easily.
factory Atom(
{String? name,
Function()? onObserved,
Function()? onUnobserved,
ReactiveContext? context}) =>
Atom._(context ?? mainContext,
name: name, onObserved: onObserved, onUnobserved: onUnobserved);
Atom._(this._context,
{String? name, Function()? onObserved, Function()? onUnobserved})
: name = name ?? _context.nameFor('Atom') {
if (onObserved != null) {
onBecomeObserved(onObserved);
}
if (onUnobserved != null) {
onBecomeUnobserved(onUnobserved);
}
}
final ReactiveContext _context;
ReactiveContext get context => _context;
final String name;
// ignore: prefer_final_fields
bool _isPendingUnobservation = false;
DerivationState _lowestObserverState = DerivationState.notTracking;
// ignore: prefer_final_fields
bool _isBeingObserved = false;
final Set<Derivation> _observers = {};
bool get hasObservers => _observers.isNotEmpty;
final Map<_ListenerKind, Set<void Function()>?> _observationListeners = {};
void reportObserved() {
_context._reportObserved(this);
}
void reportChanged() {
_context
..startBatch()
..propagateChanged(this)
..endBatch();
}
void _addObserver(Derivation d) {
_observers.add(d);
if (_lowestObserverState.index > d._dependenciesState.index) {
_lowestObserverState = d._dependenciesState;
}
}
void _removeObserver(Derivation d) {
_observers.remove(d);
if (_observers.isEmpty) {
_context._enqueueForUnobservation(this);
}
}
void _notifyOnBecomeObserved() {
final listeners = _observationListeners[_ListenerKind.onBecomeObserved];
listeners?.forEach(_notifyListener);
}
static void _notifyListener(void Function() listener) => listener();
void _notifyOnBecomeUnobserved() {
final listeners = _observationListeners[_ListenerKind.onBecomeUnobserved];
listeners?.forEach(_notifyListener);
}
void Function() onBecomeObserved(void Function() fn) =>
_addListener(_ListenerKind.onBecomeObserved, fn);
void Function() onBecomeUnobserved(void Function() fn) =>
_addListener(_ListenerKind.onBecomeUnobserved, fn);
void Function() _addListener(_ListenerKind kind, void Function() fn) {
if (_observationListeners[kind] == null) {
_observationListeners[kind] = {}..add(fn);
} else {
_observationListeners[kind]!.add(fn);
}
return () {
final listeners = _observationListeners[kind];
if (listeners == null) {
return;
}
listeners.removeWhere((f) => f == fn);
if (listeners.isEmpty) {
_observationListeners[kind] = null;
}
};
}
}
创建Atom
final _$valueAtom = Atom(name: 'CounterBase.value');
构造如下:
Atom._(context ?? mainContext,
name: name, onObserved: onObserved, onUnobserved: onUnobserved);
mainContext
定义如下:
final ReactiveContext mainContext = createContext(config: ReactiveConfig.main);
ReactiveContext createContext({ReactiveConfig? config}) =>
ReactiveContext(config: config);
维护了一个_observers
set,监听者合集。
final Set<Derivation> _observers = {};