diff --git a/packages/svelte/src/internal/client/dom/blocks/branches.js b/packages/svelte/src/internal/client/dom/blocks/branches.js index 827f9f44fa..f1b9baf6f6 100644 --- a/packages/svelte/src/internal/client/dom/blocks/branches.js +++ b/packages/svelte/src/internal/client/dom/blocks/branches.js @@ -1,5 +1,4 @@ /** @import { Effect, TemplateNode } from '#client' */ -import { is_runes } from '../../context.js'; import { Batch, current_batch } from '../../reactivity/batch.js'; import { branch, @@ -8,7 +7,6 @@ import { pause_effect, resume_effect } from '../../reactivity/effects.js'; -import { set_should_intro, should_intro } from '../../render.js'; import { hydrate_node, hydrating } from '../hydration.js'; import { create_text, should_defer_append } from '../operations.js'; @@ -126,6 +124,22 @@ export class BranchManager { } }; + /** + * @param {Batch} batch + */ + #discard = (batch) => { + this.#batches.delete(batch); + + const keys = Array.from(this.#batches.values()); + + for (const [k, branch] of this.#offscreen) { + if (!keys.includes(k)) { + destroy_effect(branch.effect); + this.#offscreen.delete(k); + } + } + }; + /** * * @param {any} key @@ -173,7 +187,8 @@ export class BranchManager { } } - batch.add_callback(this.#commit); + batch.oncommit(this.#commit); + batch.ondiscard(this.#discard); } else { if (hydrating) { this.anchor = hydrate_node; diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index a6369a7211..a0fae37133 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -310,7 +310,7 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f } } - batch.add_callback(commit); + batch.oncommit(commit); } else { commit(); } diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index de2816c333..93a3f53b23 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -108,7 +108,12 @@ export class Batch { * and append new ones by calling the functions added inside (if/each/key/etc) blocks * @type {Set<() => void>} */ - #callbacks = new Set(); + #commit_callbacks = new Set(); + + /** + * If a fork is discarded, we need to destroy any effects that are no longer needed + */ + #discard_callbacks = new Set(); /** * The number of async effects that are currently in flight @@ -328,11 +333,16 @@ export class Batch { } } + discard() { + for (const fn of this.#discard_callbacks) fn(this); + this.#discard_callbacks.clear(); + } + #resolve() { if (this.#blocking_pending === 0) { // append/remove branches - for (const fn of this.#callbacks) fn(); - this.#callbacks.clear(); + for (const fn of this.#commit_callbacks) fn(); + this.#commit_callbacks.clear(); } if (this.#pending === 0) { @@ -463,8 +473,13 @@ export class Batch { } /** @param {() => void} fn */ - add_callback(fn) { - this.#callbacks.add(fn); + oncommit(fn) { + this.#commit_callbacks.add(fn); + } + + /** @param {(batch: Batch) => void} fn */ + ondiscard(fn) { + this.#discard_callbacks.add(fn); } settled() { @@ -943,7 +958,10 @@ export function fork(fn) { await settled; }, discard: () => { - batches.delete(batch); + if (batches.has(batch)) { + batches.delete(batch); + batch.discard(); + } } }; }