diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index fce6c78b56e4..6c689dd05ea6 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -1,6 +1,12 @@ /** @import { Derived, Effect, Reaction, Signal, Source, Value } from '#client' */ import { DEV } from 'esm-env'; -import { define_property, get_descriptors, get_prototype_of, index_of } from '../shared/utils.js'; +import { + define_property, + get_descriptors, + get_prototype_of, + index_of, + run_all +} from '../shared/utils.js'; import { destroy_block_effect_children, destroy_effect_children, @@ -56,6 +62,17 @@ export function set_is_destroying_effect(value) { is_destroying_effect = value; } +/** @type {Array<() => void> | null} */ +let updated_callbacks = null; + +/** + * Run `fn` when the current reaction finishes updating + * @param {() => void} fn + */ +export function onupdated(fn) { + (updated_callbacks ??= []).push(fn); +} + // Handle effect queues /** @type {Effect[]} */ @@ -265,6 +282,7 @@ export function update_reaction(reaction) { var previous_reaction_sources = source_ownership; var previous_component_context = component_context; var previous_untracking = untracking; + var previous_updated_callbacks = updated_callbacks; var flags = reaction.f; @@ -358,6 +376,10 @@ export function update_reaction(reaction) { } catch (error) { handle_error(error); } finally { + if (updated_callbacks !== null) { + run_all(updated_callbacks); + } + new_deps = previous_deps; skipped_deps = previous_skipped_deps; untracked_writes = previous_untracked_writes; @@ -366,6 +388,7 @@ export function update_reaction(reaction) { source_ownership = previous_reaction_sources; set_component_context(previous_component_context); untracking = previous_untracking; + updated_callbacks = previous_updated_callbacks; reaction.f ^= EFFECT_IS_UPDATING; } diff --git a/packages/svelte/src/reactivity/map.js b/packages/svelte/src/reactivity/map.js index 64eb2a627629..0784cad0bcef 100644 --- a/packages/svelte/src/reactivity/map.js +++ b/packages/svelte/src/reactivity/map.js @@ -2,7 +2,7 @@ import { DEV } from 'esm-env'; import { set, source, state } from '../internal/client/reactivity/sources.js'; import { label, tag } from '../internal/client/dev/tracing.js'; -import { active_reaction, get } from '../internal/client/runtime.js'; +import { active_reaction, get, onupdated } from '../internal/client/runtime.js'; import { increment } from './utils.js'; import { teardown } from '../internal/client/reactivity/effects.js'; @@ -68,9 +68,10 @@ export class SvelteMap extends Map { if (active_reaction !== null) { this.#initial_reaction = active_reaction; + // since we only need `initial_reaction` as long as we are in a derived/effect we can // safely create a teardown function that will reset it to null and allow for GC - teardown(() => { + onupdated(() => { this.#initial_reaction = null; }); } diff --git a/packages/svelte/src/reactivity/set.js b/packages/svelte/src/reactivity/set.js index e7c429c22f69..744222afa003 100644 --- a/packages/svelte/src/reactivity/set.js +++ b/packages/svelte/src/reactivity/set.js @@ -2,7 +2,7 @@ import { DEV } from 'esm-env'; import { source, set, state } from '../internal/client/reactivity/sources.js'; import { label, tag } from '../internal/client/dev/tracing.js'; -import { active_reaction, get } from '../internal/client/runtime.js'; +import { active_reaction, get, onupdated } from '../internal/client/runtime.js'; import { increment } from './utils.js'; import { teardown } from '../internal/client/reactivity/effects.js'; @@ -63,9 +63,10 @@ export class SvelteSet extends Set { if (active_reaction !== null) { this.#initial_reaction = active_reaction; + // since we only need `initial_reaction` as long as we are in a derived/effect we can // safely create a teardown function that will reset it to null and allow for GC - teardown(() => { + onupdated(() => { this.#initial_reaction = null; }); }