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, + ` + + + +