diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index b8a0982b05..48002d759f 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -36,7 +36,7 @@ import { UNINITIALIZED } from '../../../constants.js'; import { batch_values, current_batch } from './batch.js'; import { unset_context } from './async.js'; import { deferred } from '../../shared/utils.js'; -import { set_signal_status } from './status.js'; +import { update_derived_status } from './status.js'; /** @type {Effect | null} */ export let current_async_effect = null; @@ -385,7 +385,6 @@ export function update_derived(derived) { batch_values.set(derived, value); } } else { - var status = (derived.f & CONNECTED) === 0 ? MAYBE_DIRTY : CLEAN; - set_signal_status(derived, status); + update_derived_status(derived); } } diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index fcdd389fe4..6333962f41 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -39,7 +39,7 @@ import { component_context, is_runes } from '../context.js'; import { Batch, batch_values, eager_block_effects, schedule_effect } from './batch.js'; import { proxy } from '../proxy.js'; import { execute_derived } from './deriveds.js'; -import { set_signal_status } from './status.js'; +import { set_signal_status, update_derived_status } from './status.js'; /** @type {Set} */ export let eager_effects = new Set(); @@ -218,12 +218,14 @@ export function internal_set(source, value) { } if ((source.f & DERIVED) !== 0) { + const derived = /** @type {Derived} */ (source); + // if we are assigning to a dirty derived we set it to clean/maybe dirty but we also eagerly execute it to track the dependencies if ((source.f & DIRTY) !== 0) { - execute_derived(/** @type {Derived} */ (source)); + execute_derived(derived); } - set_signal_status(source, (source.f & CONNECTED) !== 0 ? CLEAN : MAYBE_DIRTY); + update_derived_status(derived); } source.wv = increment_write_version(); diff --git a/packages/svelte/src/internal/client/reactivity/status.js b/packages/svelte/src/internal/client/reactivity/status.js index d3aaa35968..024285e73a 100644 --- a/packages/svelte/src/internal/client/reactivity/status.js +++ b/packages/svelte/src/internal/client/reactivity/status.js @@ -1,5 +1,5 @@ -/** @import { Signal } from '#client' */ -import { CLEAN, DIRTY, MAYBE_DIRTY } from '#client/constants'; +/** @import { Derived, Signal } from '#client' */ +import { CLEAN, CONNECTED, DIRTY, MAYBE_DIRTY } from '#client/constants'; const STATUS_MASK = ~(DIRTY | MAYBE_DIRTY | CLEAN); @@ -10,3 +10,16 @@ const STATUS_MASK = ~(DIRTY | MAYBE_DIRTY | CLEAN); export function set_signal_status(signal, status) { signal.f = (signal.f & STATUS_MASK) | status; } + +/** + * Set a derived's status to CLEAN or MAYBE_DIRTY based on its connection state. + * @param {Derived} derived + */ +export function update_derived_status(derived) { + // Only mark as MAYBE_DIRTY if disconnected and has dependencies. + if ((derived.f & CONNECTED) !== 0 || derived.deps === null) { + set_signal_status(derived, CLEAN); + } else { + set_signal_status(derived, MAYBE_DIRTY); + } +} diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 1b901268fc..e1a442830b 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -56,7 +56,7 @@ import { handle_error } from './error-handling.js'; import { UNINITIALIZED } from '../../constants.js'; import { captured_signals } from './legacy.js'; import { without_reactive_context } from './dom/elements/bindings/shared.js'; -import { set_signal_status } from './reactivity/status.js'; +import { set_signal_status, update_derived_status } from './reactivity/status.js'; export let is_updating_effect = false; @@ -375,16 +375,20 @@ function remove_reaction(signal, dependency) { // allows us to skip the expensive work of disconnecting and immediately reconnecting it (new_deps === null || !new_deps.includes(dependency)) ) { - set_signal_status(dependency, MAYBE_DIRTY); + const derived = /** @type {Derived} */ (dependency); + // If we are working with a derived that is owned by an effect, then mark it as being // disconnected and remove the mark flag, as it cannot be reliably removed otherwise - if ((dependency.f & CONNECTED) !== 0) { - dependency.f ^= CONNECTED; - dependency.f &= ~WAS_MARKED; + if ((derived.f & CONNECTED) !== 0) { + derived.f ^= CONNECTED; + derived.f &= ~WAS_MARKED; } + + update_derived_status(derived); + // Disconnect any reactions owned by this reaction - destroy_derived_effects(/** @type {Derived} **/ (dependency)); - remove_reactions(/** @type {Derived} **/ (dependency), 0); + destroy_derived_effects(derived); + remove_reactions(derived, 0); } }