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
pull/18105/head
Simon H 1 month ago committed by GitHub
parent 0ed8c282f9
commit 671fc2ea11
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: never mark a child effect root as inert

@ -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;
}
}

@ -0,0 +1,14 @@
<script>
export function increment() {
inc();
}
let inc;
$effect.root(() => {
let count = $state(0);
let double = $derived(count * 2);
inc = () => {
count++;
console.log('count', count, 'double', double);
}
})
</script>

@ -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]);
}
});

@ -0,0 +1,15 @@
<script>
import Child from "./Child.svelte";
let show = $state(true);
let child = $state();
let increment;
$effect(() => {
if (child) increment = child.increment;
});
</script>
<button onclick={() => show = false}>hide</button>
<button onclick={() => increment()}>increment</button>
{#if show}
<Child bind:this={child} />
{/if}
Loading…
Cancel
Save