From 686720070be0f9ace0a4efe735a0b5a6e11dd012 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 17 Nov 2025 19:26:18 +0100 Subject: [PATCH] fix: keep deriveds reactive after their original parent effect was destroyed (#17171) Use case: Remote queries that are created on one screen, then are used again on another screen. Original parent effect is destroyed in that case but derived should still be reactive. It wasn't prior to this fix because inside `get` the `destroyed` variable would be true and so deps would not properly be recorded. Fixes https://github.com/sveltejs/kit/issues/14814 --- .changeset/sad-forks-go.md | 5 ++++ .../internal/client/reactivity/deriveds.js | 7 +++-- packages/svelte/tests/signals/test.ts | 27 +++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 .changeset/sad-forks-go.md diff --git a/.changeset/sad-forks-go.md b/.changeset/sad-forks-go.md new file mode 100644 index 0000000000..da27e11642 --- /dev/null +++ b/.changeset/sad-forks-go.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: keep deriveds reactive after their original parent effect was destroyed diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 7e6f3c6f60..070230a461 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -11,7 +11,8 @@ import { STALE_REACTION, ASYNC, WAS_MARKED, - CONNECTED + CONNECTED, + DESTROYED } from '#client/constants'; import { active_reaction, @@ -296,7 +297,9 @@ function get_derived_parent_effect(derived) { var parent = derived.parent; while (parent !== null) { if ((parent.f & DERIVED) === 0) { - return /** @type {Effect} */ (parent); + // The original parent effect might've been destroyed but the derived + // is used elsewhere now - do not return the destroyed effect in that case + return (parent.f & DESTROYED) === 0 ? /** @type {Effect} */ (parent) : null; } parent = parent.parent; } diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index eff6d6166a..13430609a8 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -1391,6 +1391,33 @@ describe('signals', () => { }; }); + test('derived whose original parent effect has been destroyed keeps updating', () => { + return () => { + let count: Source; + let double: Derived; + const destroy = effect_root(() => { + render_effect(() => { + count = state(0); + double = derived(() => $.get(count) * 2); + }); + }); + + flushSync(); + assert.equal($.get(double!), 0); + + destroy(); + flushSync(); + + set(count!, 1); + flushSync(); + assert.equal($.get(double!), 2); + + set(count!, 2); + flushSync(); + assert.equal($.get(double!), 4); + }; + }); + test('$effect.root inside deriveds stay alive independently', () => { const log: any[] = []; const c = state(0);