5月27日 18:12

MobX autorun、reaction 和 when 有什么区别?

三个都是 MobX 的 reaction 工具,区别在于追踪粒度执行策略:autorun 自动追踪所有依赖并立即执行,reaction 手动指定追踪数据并延迟执行,when 只在条件满足时执行一次就自动清理。

autorun 最"懒人"——写一个函数,里面用到的 observable 变了它就重跑,创建时还会先跑一次。适合同步状态到 localStorage、更新 document.title 这类"有依赖就响应"的场景。缺点是容易多追踪,函数里不小心读了个不相关的 observable,它也会跟着重跑。

reaction 把"追踪什么"和"做什么"拆成了两个函数,第一个函数返回值变了才触发第二个。默认不会立即执行(除非设 fireImmediately: true),而且第二个函数里读的 observable 不会被追踪。适合需要精确控制触发条件的情况,比如只监听 userId 变化去加载用户数据,而不想因为 user 对象其他字段变化而重复请求。

when 是一次性的——条件函数返回 true 时执行效果函数,然后自动 dispose。适合等待初始化完成、等待数据加载这类"到了就执行,执行完就拉倒"的逻辑。如果用 autorun 或 reaction 模拟这个行为,你得手动判断条件再 dispose,容易忘。

追问

reaction 的 fireImmediately 和 autorun 有什么区别?

fireImmediately 让 effect 函数在创建时执行一次,但追踪范围仍然是第一个函数指定的,不会追踪 effect 函数里的 observable。autorun 则是把整个函数里的 observable 都追踪了。所以 fireImmediately 只是改了执行时机,没改追踪逻辑。

项目里 reaction 忘记 dispose 会怎样?

和 useEffect 忘记清理一样——组件卸载后 reaction 还在跑,继续占用内存,observable 变了还会触发回调,可能操作已卸载的组件状态,导致内存泄漏甚至报错。autorun 和 when 同理,都必须在组件卸载时调用返回的 disposer。

when 的条件一直不满足怎么办?

when 会一直监听,永不执行 effect。可以配合 setTimeout 手动调用 disposer 来设超时,或者用 when 返回的 Promise(MobX 6+)配合 Promise.race 做超时控制:

javascript
await when(() => store.loaded); // 或者带超时 await Promise.race([ when(() => store.loaded), delay(5000).then(() => { throw new Error('timeout') }) ]);

autorun 里访问数组长度和访问数组元素,追踪行为有区别吗?

有。store.items.length 只追踪 length,store.items[0] 追踪具体下标,store.items.map(...) 追踪整个数组。用 reaction 可以避免这个问题——在 data 函数里只返回你需要的数据。

写段代码

javascript
// autorun: 自动追踪,立即执行 autorun(() => { document.title = `${store.count} items`; }); // reaction: 精确追踪,延迟执行 reaction( () => store.userId, (id, prevId) => { loadProfile(id); } ); // when: 条件满足后执行一次 when( () => store.initialized, () => { startApp(); } );
标签:Mobx