-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Derive computed states only when required for delayed Reactions #3819
Conversation
🦋 Changeset detectedLatest commit: 3e56277 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
packages/mobx/src/core/reaction.ts
Outdated
if (!this.scheduledRunReaction_) { | ||
this.scheduledRunReaction_ = true | ||
this.scheduler(() => { | ||
this.scheduledRunReaction_ = false | ||
this.runReactionImpl() | ||
}) | ||
} | ||
} | ||
|
||
private runReactionImpl() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are the core changes - Reaction
dictates when it should runReactionImpl
based on the passed in scheduler.
test("autorun re-calculates computed states only when required", function (done) { | ||
let autoruns = 0 | ||
let computedCalcs = 0 | ||
|
||
const o = mobx.observable.box(0) | ||
const c = mobx.computed(() => { | ||
computedCalcs++ | ||
return o.get() | ||
}) | ||
|
||
mobx.autorun( | ||
() => { | ||
c.get() | ||
autoruns++ | ||
}, | ||
{ delay: 300 } | ||
) | ||
expect(computedCalcs).toBe(0) | ||
|
||
setTimeout(() => { | ||
// We expect the first run to run the computed | ||
expect(autoruns).toBe(1) | ||
expect(computedCalcs).toBe(1) | ||
}, 400) | ||
|
||
// We shouldn't eagerly calculate the state before the autorun runs because | ||
// the state can be mutated again before `delay` expires - if that happens, a | ||
// re-calculation here would be wasted. | ||
setTimeout(() => { | ||
o.set(1) | ||
expect(computedCalcs).toBe(1) | ||
}, 800) | ||
|
||
// This will re-invalidate the state so we'll need to recalculate | ||
// again when autorun runs (when `delay` expires). | ||
setTimeout(() => { | ||
o.set(3) | ||
expect(computedCalcs).toBe(1) | ||
}, 900) | ||
|
||
setTimeout(() => { | ||
expect(autoruns).toBe(2) | ||
expect(computedCalcs).toBe(2) | ||
done() | ||
}, 1600) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @realyze - your reproduction helped craft this test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Derive computed states only when required for delayed Reactions
Fixes #3724
Context
We use MobX with React. Due to the above issue, we are triggering unnecessary derivations for computed values (and contexts) for
autorun
s withdelay
or custom schedulers. This means that, at times, the user presses a button on screen and a render is scheduled, but, before the browser paints, it has to compute some derivation that is not required for the render.Sometimes our derivations are expensive, so we really rely on deferring the (re)calculation of computed values until a delayed
autorun
needs to read them. Moreover, without these changes, we trigger unnescessary (re)calculations prior to when a delayedautorun
runs.Changes
The core change of this PR is to lift the "scheduler" from the
autorun
andreaction
intoReaction
. The rationale for this change is (I haven't contributed to this library before, so please scrutinise these reasons closely 🙏 ):It is within
Reaction
thatshouldCompute
runs. I.e., theReaction
itself determines whether its been invalidated and needs to run itsonInvalidate
, so, I reasoned that it should also be responsible for scheduling that invalidation.mobx/packages/mobx/src/core/reaction.ts
Line 98 in df7e1eb
Benefits
Again, I am a MobX noob, so these are the perceived benefits from a novice's POV:
delay
) and only deriving them when needed.autorun
andreaction
APIs appear a bit simpler since we've consolidated the scheduling logic toReaction
.Disadvantages
Are we moving logic to Reaction that shouldn't be moved there? I think it makes sense for a Reaction to schedule its own
onInvalidation
, but I don't have the full context of the library.Code change checklist
[ ] Updated/docs
. For new functionality, at leastAPI.md
should be updatedyarn mobx test:performance
)cc @urugator