diff --git a/.changeset/tender-apples-scream.md b/.changeset/tender-apples-scream.md new file mode 100644 index 000000000000..836bdaffdf78 --- /dev/null +++ b/.changeset/tender-apples-scream.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: wrap each block expression in derived to encapsulate effects diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index 9e6405594059..b17090948ae7 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -35,8 +35,9 @@ import { source, mutable_source, internal_set } from '../../reactivity/sources.j import { array_from, is_array } from '../../../shared/utils.js'; import { INERT } from '../../constants.js'; import { queue_micro_task } from '../task.js'; -import { active_effect, active_reaction } from '../../runtime.js'; +import { active_effect, active_reaction, get } from '../../runtime.js'; import { DEV } from 'esm-env'; +import { derived_safe_equal } from '../../reactivity/deriveds.js'; /** * The row of a keyed each block that is currently updating. We track this @@ -135,15 +136,17 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f var was_empty = false; - block(() => { + // TODO: ideally we could use derived for runes mode but because of the ability + // to use a store which can be mutated, we can't do that here as mutating a store + // will still result in the collection array being the same from the store + var each_array = derived_safe_equal(() => { var collection = get_collection(); - var array = is_array(collection) - ? collection - : collection == null - ? [] - : array_from(collection); + return is_array(collection) ? collection : collection == null ? [] : array_from(collection); + }); + block(() => { + var array = get(each_array); var length = array.length; if (was_empty && length === 0) { @@ -254,7 +257,7 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f // that a mutation occurred and it's made the collection MAYBE_DIRTY, so reading the // collection again can provide consistency to the reactive graph again as the deriveds // will now be `CLEAN`. - get_collection(); + get(each_array); }); if (hydrating) { diff --git a/packages/svelte/tests/runtime-runes/samples/each-updates-9/_config.js b/packages/svelte/tests/runtime-runes/samples/each-updates-9/_config.js new file mode 100644 index 000000000000..ee35058c59bb --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/each-updates-9/_config.js @@ -0,0 +1,16 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target, logs }) { + const [btn1] = target.querySelectorAll('button'); + + btn1.click(); + flushSync(); + + await Promise.resolve(); + await Promise.resolve(); + + assert.deepEqual(logs, ['cleanup']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/each-updates-9/main.svelte b/packages/svelte/tests/runtime-runes/samples/each-updates-9/main.svelte new file mode 100644 index 000000000000..f5b2c8eb1282 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/each-updates-9/main.svelte @@ -0,0 +1,46 @@ + + + + +{#each myStore.data as _}{/each}