diff --git a/.changeset/eight-walls-mate.md b/.changeset/eight-walls-mate.md new file mode 100644 index 0000000000..a7de4e6278 --- /dev/null +++ b/.changeset/eight-walls-mate.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: simplify reaction/source ownership tracking diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index 97c8da9d33..5da1b7e188 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -1,6 +1,13 @@ /** @import { Source } from '#client' */ import { DEV } from 'esm-env'; -import { get, active_effect, active_reaction, set_active_reaction } from './runtime.js'; +import { + get, + active_effect, + update_version, + active_reaction, + set_update_version, + set_active_reaction +} from './runtime.js'; import { array_prototype, get_descriptor, @@ -41,7 +48,7 @@ export function proxy(value) { var version = source(0); var stack = DEV && tracing_mode_flag ? get_stack('CreatedAt') : null; - var reaction = active_reaction; + var parent_version = update_version; /** * Executes the proxy in the context of the reaction it was originally created in, if any @@ -49,13 +56,23 @@ export function proxy(value) { * @param {() => T} fn */ var with_parent = (fn) => { - var previous_reaction = active_reaction; - set_active_reaction(reaction); + if (update_version === parent_version) { + return fn(); + } + + // child source is being created after the initial proxy — + // prevent it from being associated with the current reaction + var reaction = active_reaction; + var version = update_version; + + set_active_reaction(null); + set_update_version(parent_version); - /** @type {T} */ var result = fn(); - set_active_reaction(previous_reaction); + set_active_reaction(reaction); + set_update_version(version); + return result; }; diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 9f08354cc0..f84312e31c 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -11,7 +11,7 @@ import { untrack, increment_write_version, update_effect, - source_ownership, + current_sources, check_dirtiness, untracking, is_destroying_effect, @@ -140,7 +140,7 @@ export function set(source, value, should_proxy = false) { (!untracking || (active_reaction.f & INSPECT_EFFECT) !== 0) && is_runes() && (active_reaction.f & (DERIVED | BLOCK_EFFECT | INSPECT_EFFECT)) !== 0 && - !(source_ownership?.reaction === active_reaction && source_ownership.sources.includes(source)) + !current_sources?.includes(source) ) { e.state_unsafe_mutation(); } diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index d6e7325ba3..6477e2942a 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -88,17 +88,17 @@ export function set_active_effect(effect) { /** * When sources are created within a reaction, reading and writing * them within that reaction should not cause a re-run - * @type {null | { reaction: Reaction, sources: Source[] }} + * @type {null | Source[]} */ -export let source_ownership = null; +export let current_sources = null; /** @param {Value} value */ export function push_reaction_value(value) { if (active_reaction !== null && active_reaction.f & EFFECT_IS_UPDATING) { - if (source_ownership === null) { - source_ownership = { reaction: active_reaction, sources: [value] }; + if (current_sources === null) { + current_sources = [value]; } else { - source_ownership.sources.push(value); + current_sources.push(value); } } } @@ -136,6 +136,11 @@ let read_version = 0; export let update_version = read_version; +/** @param {number} value */ +export function set_update_version(value) { + update_version = value; +} + // If we are working with a get() chain that has no active container, // to prevent memory leaks, we skip adding the reaction. export let skip_reaction = false; @@ -236,7 +241,7 @@ function schedule_possible_effect_self_invalidation(signal, effect, root = true) var reactions = signal.reactions; if (reactions === null) return; - if (source_ownership?.reaction === active_reaction && source_ownership.sources.includes(signal)) { + if (current_sources?.includes(signal)) { return; } @@ -263,7 +268,7 @@ export function update_reaction(reaction) { var previous_untracked_writes = untracked_writes; var previous_reaction = active_reaction; var previous_skip_reaction = skip_reaction; - var previous_reaction_sources = source_ownership; + var previous_sources = current_sources; var previous_component_context = component_context; var previous_untracking = untracking; var previous_update_version = update_version; @@ -277,7 +282,7 @@ export function update_reaction(reaction) { (flags & UNOWNED) !== 0 && (untracking || !is_updating_effect || active_reaction === null); active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; - source_ownership = null; + current_sources = null; set_component_context(reaction.ctx); untracking = false; update_version = ++read_version; @@ -365,7 +370,7 @@ export function update_reaction(reaction) { untracked_writes = previous_untracked_writes; active_reaction = previous_reaction; skip_reaction = previous_skip_reaction; - source_ownership = previous_reaction_sources; + current_sources = previous_sources; set_component_context(previous_component_context); untracking = previous_untracking; update_version = previous_update_version; @@ -759,10 +764,7 @@ export function get(signal) { // Register the dependency on the current reaction signal. if (active_reaction !== null && !untracking) { - if ( - source_ownership?.reaction !== active_reaction || - !source_ownership?.sources.includes(signal) - ) { + if (!current_sources?.includes(signal)) { var deps = active_reaction.deps; if (signal.rv < read_version) { signal.rv = read_version;