Limits of MobX reaction deferral inside actions in version 6
0 reputation · 14 Oct 2021, 06:20 UTC
0 reputation · 14 Oct 2021, 06:20 UTC
Goal: Observe whether a reaction can be notified synchronously after an observable change that occurs inside an action, while keeping the default batching behavior for other reactions.
Constraints: In MobX 6 reactions are scheduled as micro‑tasks by default, and all changes made inside an action are batched so that reactions run only after the action finishes. Disabling the global reaction scheduler forces synchronous execution for every reaction, which may break existing assumptions and affect performance.
Uncertainty: The core team has discussed allowing certain observables or reactions to opt‑in to immediate notification without turning off the scheduler globally, but no official API exists yet.
Questions:
35275 reputation · 14 Oct 2021, 16:02 UTC
No, MobX 6 does not provide a built-in configuration or annotation to make specific reactions run synchronously while maintaining the global asynchronous micro-task scheduler. All reactions triggered by observable changes inside an action are queued and deferred until the action completes.
In MobX 6, actions create a transaction boundary. When an observable is modified within this boundary, MobX marks the dependent reactions as stale but does not execute them immediately. This prevents "glitches" (temporary inconsistent states) and redundant renders. Because the scheduler is global, you cannot opt-out a single reaction from this batching behavior without affecting the entire application.
If a specific piece of logic must execute immediately upon a change, consider these two patterns:
enforceActions is not set to "always") trigger reactions immediately.// Example: Manual execution instead of reaction deferral
action updateData() {
this.value = "new value";
// Instead of waiting for a reaction, call the logic explicitly
this.syncDependentLogic();
}
Permitting synchronous reactions—either by disabling the global scheduler or bypassing actions—introduces several risks:
To verify the deferral behavior in your environment, you can use a simple log test:
import { observable, action, reaction } from "mobx";
const store = observable({ count: 0 });
reaction(
() => store.count,
() => console.log("Reaction executed")
);
const increment = action(() => {
console.log("Action start");
store.count++;
console.log("Action end");
});
increment();
// Expected Output:
// "Action start"
// "Action end"
// "Reaction executed" (deferred until after action)
Diagnostic Detail: Are you using a custom scheduler or a specific wrapper (like mobx-react-lite) that might be introducing additional batching layers?
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.