diff --git a/.changeset/lucky-pears-smoke.md b/.changeset/lucky-pears-smoke.md new file mode 100644 index 0000000000..816a37dca5 --- /dev/null +++ b/.changeset/lucky-pears-smoke.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: give effect teardowns the value from before the first write in a flush diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 5becae2dc9..8251f79298 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -420,6 +420,7 @@ export class Batch { } if (next_batch !== null) { + old_values.clear(); next_batch.#process(); } } diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 218941ab67..70be6489a4 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -180,7 +180,13 @@ export function set(source, value, should_proxy = false) { */ export function internal_set(source, value, updated_during_traversal = null) { if (!source.equals(value)) { - old_values.set(source, is_destroying_effect ? value : source.v); + if (is_destroying_effect) { + old_values.set(source, value); + } else if (!old_values.has(source)) { + // only record the value from before the first write in this flush, otherwise a + // teardown would see the value from before whichever write happened to be last + old_values.set(source, source.v); + } var batch = Batch.ensure(); batch.capture(source, value); diff --git a/packages/svelte/tests/runtime-runes/samples/effect-teardown-multiple-writes/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-teardown-multiple-writes/_config.js new file mode 100644 index 0000000000..7db4966106 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-teardown-multiple-writes/_config.js @@ -0,0 +1,17 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target, logs }) { + const button = target.querySelector('button'); + + flushSync(() => button?.click()); + + assert.deepEqual(logs, [ + 'register: one', + 'unregister: one', + 'leftover: none', + 'register: three' + ]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/effect-teardown-multiple-writes/main.svelte b/packages/svelte/tests/runtime-runes/samples/effect-teardown-multiple-writes/main.svelte new file mode 100644 index 0000000000..a3932aaa22 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-teardown-multiple-writes/main.svelte @@ -0,0 +1,21 @@ + + +