From d3a01bd5a7d09384e29a599834d9b554a7a973ee Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 20 Jul 2025 17:19:56 -0400 Subject: [PATCH] fix: always mark reactions of deriveds (#16457) * tweak * fix * test * changeset --- .changeset/angry-hornets-hug.md | 5 ++ .../internal/client/reactivity/deriveds.js | 4 +- .../src/internal/client/reactivity/sources.js | 19 +++---- .../async-time-travelling-derived/_config.js | 56 +++++++++++++++++++ .../async-time-travelling-derived/main.svelte | 26 +++++++++ 5 files changed, 98 insertions(+), 12 deletions(-) create mode 100644 .changeset/angry-hornets-hug.md create mode 100644 packages/svelte/tests/runtime-runes/samples/async-time-travelling-derived/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-time-travelling-derived/main.svelte diff --git a/.changeset/angry-hornets-hug.md b/.changeset/angry-hornets-hug.md new file mode 100644 index 0000000000..ffe59db100 --- /dev/null +++ b/.changeset/angry-hornets-hug.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: always mark reactions of deriveds diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index fa6a9e02a1..7f730e365e 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -337,7 +337,9 @@ export function update_derived(derived) { // don't mark derived clean if we're reading it inside a // cleanup function, or it will cache a stale value - if (is_destroying_effect) return; + if (is_destroying_effect) { + return; + } if (batch_deriveds !== null) { batch_deriveds.set(derived, derived.v); diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 9b534d2d71..f6b14f3360 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -314,9 +314,6 @@ function mark_reactions(signal, status) { var reaction = reactions[i]; var flags = reaction.f; - // Skip any effects that are already dirty - if ((flags & DIRTY) !== 0) continue; - // In legacy mode, skip the current effect to prevent infinite loops if (!runes && reaction === active_effect) continue; @@ -326,15 +323,15 @@ function mark_reactions(signal, status) { continue; } - set_signal_status(reaction, status); + // don't set a DIRTY reaction to MAYBE_DIRTY + if ((flags & DIRTY) === 0) { + set_signal_status(reaction, status); + } - // If the signal a) was previously clean or b) is an unowned derived, then mark it - if ((flags & (CLEAN | UNOWNED)) !== 0) { - if ((flags & DERIVED) !== 0) { - mark_reactions(/** @type {Derived} */ (reaction), MAYBE_DIRTY); - } else { - schedule_effect(/** @type {Effect} */ (reaction)); - } + if ((flags & DERIVED) !== 0) { + mark_reactions(/** @type {Derived} */ (reaction), MAYBE_DIRTY); + } else if ((flags & DIRTY) === 0) { + schedule_effect(/** @type {Effect} */ (reaction)); } } } diff --git a/packages/svelte/tests/runtime-runes/samples/async-time-travelling-derived/_config.js b/packages/svelte/tests/runtime-runes/samples/async-time-travelling-derived/_config.js new file mode 100644 index 0000000000..06437d2e31 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-time-travelling-derived/_config.js @@ -0,0 +1,56 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + await tick(); + + const [a, b, update] = target.querySelectorAll('button'); + + assert.htmlEqual( + target.innerHTML, + ` + + + +

a

+ ` + ); + + b.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + + +

b

+ ` + ); + + update.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + + +

b

+ ` + ); + + a.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + + + +

a

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-time-travelling-derived/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-time-travelling-derived/main.svelte new file mode 100644 index 0000000000..5fca286a78 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-time-travelling-derived/main.svelte @@ -0,0 +1,26 @@ + + + + + + + + + {#if condition} +

a

+ {:else} +

b

+ {/if} + + {#snippet pending()}{/snippet} +
+