diff --git a/.changeset/early-crabs-rest.md b/.changeset/early-crabs-rest.md new file mode 100644 index 0000000000..d3f290a5c2 --- /dev/null +++ b/.changeset/early-crabs-rest.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: abort deriveds own AbortSignal when it disconnects diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index fd64f3b45d..0410280100 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -28,6 +28,7 @@ import { skipped_deps, new_deps } from '../runtime.js'; +import { without_reactive_context } from '../dom/elements/bindings/shared.js'; import { equals, safe_equals } from './equality.js'; import * as e from '../errors.js'; import * as w from '../warnings.js'; @@ -444,6 +445,13 @@ export function update_derived(derived) { * @param {Derived} derived */ export function freeze_derived_effects(derived) { + if (derived.ac !== null) { + without_reactive_context(() => { + derived.ac?.abort(STALE_REACTION); + derived.ac = null; + }); + } + if (derived.effects === null) return; for (const e of derived.effects) { diff --git a/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/Child.svelte b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/Child.svelte new file mode 100644 index 0000000000..dca1206949 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/Child.svelte @@ -0,0 +1,20 @@ + + +{der} diff --git a/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/_config.js b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/_config.js new file mode 100644 index 0000000000..d52fb442d0 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/_config.js @@ -0,0 +1,14 @@ +import { ok, test } from '../../test'; +import { flushSync } from 'svelte'; + +export default test({ + async test({ assert, target, errors }) { + const btn = target.querySelector('button'); + + flushSync(() => { + btn?.click(); + }); + assert.htmlEqual(target.innerHTML, '1 '); + assert.deepEqual(errors, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/main.svelte b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/main.svelte new file mode 100644 index 0000000000..3997fcc35d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-destroy/main.svelte @@ -0,0 +1,14 @@ + + +{aborted} + + + +{#if count % 2 === 0} + +{/if} diff --git a/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/_config.js b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/_config.js index 2dacf188d7..aa38dd8280 100644 --- a/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/_config.js @@ -4,9 +4,11 @@ import { flushSync } from 'svelte'; export default test({ async test({ assert, target, errors }) { const btn = target.querySelector('button'); + flushSync(() => { btn?.click(); }); + assert.htmlEqual(target.innerHTML, '1:1 '); assert.deepEqual(errors, []); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/main.svelte b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/main.svelte index ebefe38fb2..b1076926e3 100644 --- a/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/main.svelte @@ -19,6 +19,6 @@ }) -{der} +{der}:{aborted} - \ No newline at end of file +