Can disposing a MobX reaction in useEffect cleanup miss observable updates between unmount and remount?
0 reputation · 13 Dec 2023, 14:04 UTC
0 reputation · 13 Dec 2023, 14:04 UTC
In MobX 6, reactions created with autorun, reaction, or when return a single-use disposer that permanently unsubscribes from all tracked observables. In a React component, it is common to create a reaction inside useEffect and dispose it in the returned cleanup function. Because the disposer cannot be reused, every remount requires creating an entirely new reaction instance.
The concern is the interval between cleanup disposal and the next effect run. If an observable changes after cleanup has disposed the old reaction but before the new reaction is created, it is unclear whether that intermediate change is silently dropped or whether the newly created reaction reads the current value without replaying the missed transition. This matters for derived state that must stay consistent across component lifecycles.
Does a fresh MobX 6 reaction created after disposal always observe the latest observable value, or can changes during the disposal-to-recreation gap be lost? When a reaction is disposed inside its own effect rather than in an external cleanup, does that alter the timing of unsubscription relative to subsequent observable mutations? Is there a documented guarantee about atomicity between disposing one reaction and instantiating another in the same tick?
35775 reputation · 14 Dec 2023, 01:21 UTC
In MobX 6, disposing a reaction inside a useEffect cleanup creates a gap between permanent unsubscription and the next effect run. Any observable change that occurs after the disposer runs but before the new reaction is created is not seen by the new instance.
Because JavaScript is single-threaded, the disposal call, any pending microtasks, and the new reaction creation execute sequentially. If an observable mutates between the moment the old disposer returns and the moment the new reaction calls its observer function, the new reaction starts with the value present at its first evaluation, missing the intermediate transition.
If you can confirm whether the observable change occurs before the cleanup function returns or after the component unmounts, I can narrow the diagnostic further.
Use comments to ask for clarification. Post a solution as an answer.
35,775 reputation · 13 Dec 2023, 23:02 UTC
React runs useEffect cleanup synchronously during unmount, then schedules the next effect after the subsequent render. Between those two points the component is unmounted and no reaction exists. Any observable mutation in that window—whether from a state update, timer, or external store—is not delivered to the new reaction because MobX disposers are permanent and fresh reactions only read the current value at creation time.
There is no atomic "swap" guarantee; the old disposer finishes, the component unmounts, React paints, then the new effect runs. If you need continuity, keep the disposer in a useRef and only replace it when the observable key actually changes, or lift the reaction to a parent that stays mounted.