diff --git a/packages/svelte/src/internal/client/constants.js b/packages/svelte/src/internal/client/constants.js index 50a7a21ae80f..11956e3ecb2c 100644 --- a/packages/svelte/src/internal/client/constants.js +++ b/packages/svelte/src/internal/client/constants.js @@ -25,6 +25,7 @@ export const REACTION_IS_UPDATING = 1 << 21; export const ASYNC = 1 << 22; export const ERROR_VALUE = 1 << 23; +export const HAS_EFFECTS = 1 << 24; export const STATE_SYMBOL = Symbol('$state'); export const LEGACY_PROPS = Symbol('legacy props'); diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 68a155503237..62d2f04f4d7c 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -33,7 +33,8 @@ import { EFFECT_PRESERVED, STALE_REACTION, USER_EFFECT, - ASYNC + ASYNC, + HAS_EFFECTS } from '#client/constants'; import * as e from '../errors.js'; import { DEV } from 'esm-env'; @@ -156,6 +157,7 @@ function create_effect(type, fn, sync, push = true) { ) { var derived = /** @type {Derived} */ (active_reaction); (derived.effects ??= []).push(effect); + derived.f |= HAS_EFFECTS; } } diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 22a1890e0f50..a71e2d358863 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -20,7 +20,8 @@ import { DISCONNECTED, REACTION_IS_UPDATING, STALE_REACTION, - ERROR_VALUE + ERROR_VALUE, + HAS_EFFECTS } from './constants.js'; import { old_values } from './reactivity/sources.js'; import { @@ -671,6 +672,9 @@ export function get(signal) { if (is_dirty(derived)) { update_derived(derived); + } else if ((derived.f & HAS_EFFECTS) !== 0 && derived.effects === null) { + // Recreate effects they have been destroyed without turning the derived dirty. + update_derived(derived); } } diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index eff6d6166a5e..67408d3f155f 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -1441,4 +1441,50 @@ describe('signals', () => { assert.deepEqual(log, ['inner destroyed', 'inner destroyed']); }; }); + + test('derived effects reconnect correctly', () => { + const log: string[] = []; + let a: Derived; + + return () => { + const destroy1 = effect_root(() => { + a = derived(() => { + user_effect(() => { + log.push('effect-executed'); + }); + return 42; + }); + }); + + const destroy2 = effect_root(() => { + render_effect(() => { + $.get(a); + }); + }); + + assert.equal(log.length, 0); + assert.equal(a?.effects?.length, 1); + + destroy2(); + flushSync(); + + assert.equal(a?.effects, null); + assert.equal(log.length, 0); + + const destroy3 = effect_root(() => { + render_effect(() => { + const value = $.get(a); + assert.equal(value, 42); + }); + }); + + flushSync(); + + assert.equal(log.length, 1); + assert.equal(a?.effects?.length, 1); + + destroy3(); + destroy1(); + }; + }); });