From 671fc2ea11b56f050f37f7e03564fb070bc8abea Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 14 Apr 2026 14:47:56 +0200 Subject: [PATCH] fix: never mark a child effect root as inert (#18111) A nested `$effect.root` was marked `INERT` during `pause_children`, which caused it to stay in that state indefinetly after the rest of the parent tree was destroyed. Consequently deriveds inside no longer update and cause warnings. This fixes it by not marking nested `$effect.root`s as inert, just like nested `$effect.root`s are not destryoed and instead become a new root. Fixes #18097 --- .changeset/fresh-chicken-itch.md | 5 ++++ .../src/internal/client/reactivity/effects.js | 26 ++++++++++++------- .../samples/effect-root-6/Child.svelte | 14 ++++++++++ .../samples/effect-root-6/_config.js | 14 ++++++++++ .../samples/effect-root-6/main.svelte | 15 +++++++++++ 5 files changed, 64 insertions(+), 10 deletions(-) create mode 100644 .changeset/fresh-chicken-itch.md create mode 100644 packages/svelte/tests/runtime-runes/samples/effect-root-6/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/effect-root-6/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/effect-root-6/main.svelte diff --git a/.changeset/fresh-chicken-itch.md b/.changeset/fresh-chicken-itch.md new file mode 100644 index 0000000000..95120e1591 --- /dev/null +++ b/.changeset/fresh-chicken-itch.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: never mark a child effect root as inert diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index ea8a4b645e..0fad074e6f 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -654,16 +654,22 @@ function pause_children(effect, transitions, local) { while (child !== null) { var sibling = child.next; - var transparent = - (child.f & EFFECT_TRANSPARENT) !== 0 || - // If this is a branch effect without a block effect parent, - // it means the parent block effect was pruned. In that case, - // transparency information was transferred to the branch effect. - ((child.f & BRANCH_EFFECT) !== 0 && (effect.f & BLOCK_EFFECT) !== 0); - // TODO we don't need to call pause_children recursively with a linked list in place - // it's slightly more involved though as we have to account for `transparent` changing - // through the tree. - pause_children(child, transitions, transparent ? local : false); + + // If this child is a root effect, then it will become an independent root when its parent + // is destroyed, it should therefore not become inert nor partake in transitions. + if ((child.f & ROOT_EFFECT) === 0) { + var transparent = + (child.f & EFFECT_TRANSPARENT) !== 0 || + // If this is a branch effect without a block effect parent, + // it means the parent block effect was pruned. In that case, + // transparency information was transferred to the branch effect. + ((child.f & BRANCH_EFFECT) !== 0 && (effect.f & BLOCK_EFFECT) !== 0); + // TODO we don't need to call pause_children recursively with a linked list in place + // it's slightly more involved though as we have to account for `transparent` changing + // through the tree. + pause_children(child, transitions, transparent ? local : false); + } + child = sibling; } } diff --git a/packages/svelte/tests/runtime-runes/samples/effect-root-6/Child.svelte b/packages/svelte/tests/runtime-runes/samples/effect-root-6/Child.svelte new file mode 100644 index 0000000000..110ad82b0d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-root-6/Child.svelte @@ -0,0 +1,14 @@ + diff --git a/packages/svelte/tests/runtime-runes/samples/effect-root-6/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-root-6/_config.js new file mode 100644 index 0000000000..e4077c6c27 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-root-6/_config.js @@ -0,0 +1,14 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +// Test that $effect.root continues to be operational after its parent effect has been destroyed +export default test({ + test({ assert, target, logs }) { + const [hide, increment] = target.querySelectorAll('button'); + + hide.click(); + flushSync(); + increment.click(); + assert.deepEqual(logs, ['count', 1, 'double', 2]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/effect-root-6/main.svelte b/packages/svelte/tests/runtime-runes/samples/effect-root-6/main.svelte new file mode 100644 index 0000000000..254cddeb5b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-root-6/main.svelte @@ -0,0 +1,15 @@ + + + + +{#if show} + +{/if}