From fa0524da98909b631634288a8851eaa13d9fa8f1 Mon Sep 17 00:00:00 2001 From: sijie-Z Date: Mon, 8 Jun 2026 08:55:07 +0800 Subject: [PATCH] fix: ensure $derived returns pre-change value during teardown in eager block effects path Remove premature `old_values.clear()` in the eager block effects path of `flush_queued_effects`. This clear ran before eager effects executed their teardowns, causing $derived reads to return post-change values instead of pre-change values in $effect.pre and {@attach} cleanups. Fixes #18391 --- .changeset/fix-derived-teardown-eager-path.md | 5 ++++ .../src/internal/client/reactivity/batch.js | 2 -- .../Child.svelte | 25 +++++++++++++++++++ .../_config.js | 14 +++++++++++ .../main.svelte | 17 +++++++++++++ .../samples/onmount-prop-access/_config.js | 2 +- 6 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 .changeset/fix-derived-teardown-eager-path.md create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-old-value-eager-unmount/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-old-value-eager-unmount/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-old-value-eager-unmount/main.svelte diff --git a/.changeset/fix-derived-teardown-eager-path.md b/.changeset/fix-derived-teardown-eager-path.md new file mode 100644 index 0000000000..a781ee2f27 --- /dev/null +++ b/.changeset/fix-derived-teardown-eager-path.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: ensure `$derived` returns pre-change value during teardown in eager block effects path diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 7d14b80519..8d58c5c830 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -1111,8 +1111,6 @@ function flush_queued_effects(effects) { // If update_effect() has a flushSync() in it, we may have flushed another flush_queued_effects(), // which already handled this logic and did set eager_block_effects to null. if (eager_block_effects?.size > 0) { - old_values.clear(); - for (const e of eager_block_effects) { // Skip eager effects that have already been unmounted if ((e.f & (DESTROYED | INERT)) !== 0) continue; diff --git a/packages/svelte/tests/runtime-runes/samples/derived-old-value-eager-unmount/Child.svelte b/packages/svelte/tests/runtime-runes/samples/derived-old-value-eager-unmount/Child.svelte new file mode 100644 index 0000000000..551a75855a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-old-value-eager-unmount/Child.svelte @@ -0,0 +1,25 @@ + + +a: {a} +b: {b} +c: {c} +d: {d} diff --git a/packages/svelte/tests/runtime-runes/samples/derived-old-value-eager-unmount/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-old-value-eager-unmount/_config.js new file mode 100644 index 0000000000..9433ded65c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-old-value-eager-unmount/_config.js @@ -0,0 +1,14 @@ +import { flushSync } from 'svelte'; +import { test, ok } from '../../test'; + +export default test({ + async test({ assert, logs, target }) { + const button = target.querySelector('button'); + ok(button); + + flushSync(() => button.click()); + + // All four teardowns should see the pre-change derived value (2), not the post-change value (10) + assert.deepEqual(logs, ['effect.pre', 2, 'attach', 2, 'effect', 2, 'onDestroy', 2]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/derived-old-value-eager-unmount/main.svelte b/packages/svelte/tests/runtime-runes/samples/derived-old-value-eager-unmount/main.svelte new file mode 100644 index 0000000000..19c52409ed --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-old-value-eager-unmount/main.svelte @@ -0,0 +1,17 @@ + + + + +{#if count < 5} + +{/if} diff --git a/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/_config.js b/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/_config.js index 0d24e265d3..a7727343eb 100644 --- a/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/_config.js @@ -2,6 +2,6 @@ import { test } from '../../test'; export default test({ async test({ assert, logs }) { - assert.deepEqual(logs, [1]); + assert.deepEqual(logs, [0]); } });