diff --git a/.changeset/svelte18489.md b/.changeset/svelte18489.md new file mode 100644 index 0000000000..161404b93b --- /dev/null +++ b/.changeset/svelte18489.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: recompute dirty owned deriveds after owner teardown diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index fd64f3b45d..0261dfafd4 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -347,9 +347,13 @@ export function execute_derived(derived) { derived.v !== UNINITIALIZED && // if it was never evaluated before, it's guaranteed to fail downstream, so we try to execute instead (parent.f & (DESTROYED | INERT)) !== 0 ) { - w.derived_inert(); + if (derived.rv === 0) { + w.derived_inert(); - return derived.v; + return derived.v; + } + + parent = null; } set_active_effect(parent); diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index 927ce2e665..4154cc627d 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -1428,6 +1428,58 @@ describe('signals', () => { }; }); + test('class field derived updates after original parent effect is destroyed', () => { + return () => { + const warn = console.warn; + const warnings: string[] = []; + console.warn = (...args) => { + warnings.push(...args.map(String)); + }; + + class Box { + value = state('A'); + doubled = derived(() => $.get(this.value) + $.get(this.value)); + + get result() { + return $.get(this.doubled); + } + } + + let box: Box | undefined; + const rendered: string[] = []; + + try { + const destroy = effect_root(() => { + render_effect(() => { + let instance = box; + if (instance === undefined) { + instance = box = new Box(); + } + + rendered.push(instance.result); + }); + }); + + flushSync(); + assert.deepEqual(rendered, ['AA']); + + flushSync(() => set(box!.value, 'B')); + assert.deepEqual(rendered, ['AA', 'BB']); + + destroy(); + + flushSync(() => set(box!.value, 'C')); + assert.equal(box!.result, 'CC'); + assert.deepEqual( + warnings.filter((warning) => warning.includes('derived_inert')), + [] + ); + } finally { + console.warn = warn; + } + }; + }); + test('derived when connected should add new dependency to its reaction even when read outside effect', () => { let count_a = state(0); let count_b = state(0);