Feedback Wanted — change: run effects after a tick #1680
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
The Problem
Currently, effects created with
create_effect
runThis means that simple code like this fails silently
This logs
focusing
but does not actually focus the<input>
because it has not yet been mounted to the DOM. The sequence of events goes:create_node_ref
: NodeRef is createdcreate_effect
: creates the effect, and runs once immediately to register its dependencies<input node_ref=input/>
: creates anHtmlInputElement
and loads it into theNodeRef
NodeRef
triggers the effect to run again, callinginput.focus()
HtmlInputElement
is returned and mounted to the DOMBecause 4 happens before 5 — i.e.,
.focus()
is called before the element has actually been mounted — the browser does not focus it.The Solution
This PR waits to run an effect for the first time until one tick after it's created (determined by
queueMicrotask
: i.e., it runs it after all other synchronous code finishes running.)This means the same code now runs in the following order:
create_node_ref
: NodeRef is createdcreate_effect
: creates the effect, and queues first run<input node_ref=input/>
: creates anHtmlInputElement
and loads it into theNodeRef
NodeRef
does nothing, because the effect hasn't run yet so it hasn't registered the dependencyHtmlInputElement
is returned and mounted to the DOMThis also means the effect only ends up running once, for what it's worth.
This is a breaking change, as it affects the scheduling of effects. However, it tends to schedule effects so that they are closer to what people already assume: they run after the view has been returned and the component mounted, not immediately (i.e., before it.)
This should fix almost every situation in which the current recommendation is
This is how SolidJS runs its effects as well, and it distinguishes between
createEffect()
(user-created effect, runs on next tick) andcreateRenderEffect()
(used internally in renderer, runs immediately.) We would do the same, using immediate synchronous effects in the renderer and delayed effects as the default for user code.