fix: correctly coordinate component-level effects inside async blocks (#18260)

While looking at the reproduction in
https://github.com/sveltejs/svelte/issues/18221#issuecomment-4497918414
I immediately got greeted with a runtime error when running it in the
playground (weirdly not in the Stackblitz version). The error was that a
component expected a binding to be set in onMount, but the timing of
onMount was wrong.

Turns out it's because our logic to determine whether or not to defer
top level effects is flawed. `REACTION_RAN`, which was used previously,
is already set if the initialized component is inside an async block. We
instead check for `component_context.i` which is set to `true` on
`pop()`.
pull/18271/head
Simon H 2 months ago committed by GitHub
parent 65283bc13a
commit 91a42e2ed6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: correctly coordinate component-level effects inside async blocks

@ -20,7 +20,6 @@ import {
EFFECT,
DESTROYED,
INERT,
REACTION_RAN,
BLOCK_EFFECT,
ROOT_EFFECT,
EFFECT_TRANSPARENT,
@ -213,7 +212,11 @@ export function user_effect(fn) {
// Non-nested `$effect(...)` in a component should be deferred
// until the component is mounted
var flags = /** @type {Effect} */ (active_effect).f;
var defer = !active_reaction && (flags & BRANCH_EFFECT) !== 0 && (flags & REACTION_RAN) === 0;
var defer =
!active_reaction &&
(flags & BRANCH_EFFECT) !== 0 &&
component_context !== null &&
!component_context.i;
if (defer) {
// Top-level `$effect(...)` in an unmounted component — defer until mount

@ -0,0 +1,6 @@
<script>
let x;
$effect(() => console.log(!!x))
</script>
<div bind:this={x}></div>

@ -0,0 +1,10 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
// Test that $effect/onMount etc at the top level of components are correctly deferred/coordinated if inside an async block
async test({ assert, logs }) {
await tick();
assert.deepEqual(logs, [true]);
}
});

@ -0,0 +1,5 @@
<script>
import Child from './Child.svelte';
</script>
<Child foo={await 1} />
Loading…
Cancel
Save