diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 2a0e721fa5..c72fded0cd 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -1,4 +1,3 @@ -import { DEV } from 'esm-env'; import { CLEAN, DERIVED, DESTROYED, DIRTY, MAYBE_DIRTY, UNOWNED } from '../constants.js'; import { current_reaction, @@ -102,7 +101,7 @@ export function update_derived(derived, force_schedule) { derived.v = value; derived.version = increment_version(); - mark_reactions(derived, DIRTY, force_schedule); + mark_reactions(derived, force_schedule); } } diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 233351c632..da713bcc01 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -10,7 +10,6 @@ import { mark_reactions, schedule_effect, set_current_untracked_writes, - set_last_inspected_signal, set_signal_status, untrack, increment_version, @@ -95,7 +94,7 @@ export function set(source, value) { source.v = value; source.version = increment_version(); - mark_reactions(source, DIRTY, true); + mark_reactions(source, true); // If the current signal is running for the first time, it won't have any // reactions as we only allocate and assign the reactions after the signal diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index d2f7d6a36d..126caaf90f 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -851,19 +851,25 @@ export function invalidate_inner_signals(fn) { /** * @param {import('#client').Value} signal - * @param {number} to_status should be DIRTY or MAYBE_DIRTY * @param {boolean} force_schedule * @returns {void} */ -export function mark_reactions(signal, to_status, force_schedule) { +export function mark_reactions(signal, force_schedule) { var reactions = signal.reactions; if (reactions === null) return; var runes = is_runes(); - var length = reactions.length; + var stack = reactions.slice(); + + var n = stack.length; + + while (stack.length > 0) { + // top-level reactions are DIRTY, others are MAYBE_DIRTY + var status = n < stack.length ? MAYBE_DIRTY : DIRTY; + + var reaction = /** @type {import('#client').Reaction} */ (stack.pop()); + if (stack.length < n) n = stack.length; - for (var i = 0; i < length; i++) { - var reaction = reactions[i]; var flags = reaction.f; if (DEV && (flags & INSPECT_EFFECT) !== 0) { @@ -878,7 +884,7 @@ export function mark_reactions(signal, to_status, force_schedule) { continue; } - set_signal_status(reaction, to_status); + set_signal_status(reaction, status); // If the signal is not clean, then skip over it – with the exception of unowned signals that // are already maybe dirty. Unowned signals might be dirty because they are not captured as part of an @@ -888,11 +894,12 @@ export function mark_reactions(signal, to_status, force_schedule) { if ((flags & CLEAN) !== 0 || (maybe_dirty && unowned)) { if ((reaction.f & DERIVED) !== 0) { - mark_reactions( - /** @type {import('#client').Derived} */ (reaction), - MAYBE_DIRTY, - force_schedule - ); + // Push the derived reaction's reactions onto the stack with MAYBE_DIRTY status + var children = /** @type {import('#client').Derived} */ (reaction).reactions; + + if (children !== null) { + stack.push(...children); + } } else { schedule_effect(/** @type {import('#client').Effect} */ (reaction)); }