diff --git a/.changeset/calm-derived-reconnect.md b/.changeset/calm-derived-reconnect.md new file mode 100644 index 0000000000..22aa5e9357 --- /dev/null +++ b/.changeset/calm-derived-reconnect.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent duplicate reactions when reconnecting deriveds diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index d86c349445..15707533ac 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -736,7 +736,10 @@ function reconnect(derived) { if (derived.deps === null) return; for (const dep of derived.deps) { - (dep.reactions ??= []).push(derived); + var reactions = (dep.reactions ??= []); + if (!includes.call(reactions, derived)) { + reactions.push(derived); + } if ((dep.f & DERIVED) !== 0 && (dep.f & CONNECTED) === 0) { unfreeze_derived_effects(/** @type {Derived} */ (dep)); diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index 285dc1b3ce..09a692c742 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -810,6 +810,30 @@ describe('signals', () => { }; }); + test('deriveds with changed dependencies clean up after reconnection', () => { + const visible = state(true); + const first = state(0); + const second = state(1); + const selected = state(first); + const value = derived(() => $.get($.get(selected))); + + return () => { + const destroy = effect_root(() => { + render_effect(() => { + if ($.get(visible)) $.get(value); + }); + }); + + flushSync(() => set(visible, false)); + flushSync(() => set(selected, second)); + flushSync(() => set(visible, true)); + + destroy(); + + assert.equal(second.reactions, null); + }; + }); + test('deriveds update upon reconnection #1', () => { let a = state(false); let b = state(false);