From 531ff6243c23e53b7a0dd2f6d3661e67cf0cab3b Mon Sep 17 00:00:00 2001 From: adiGuba Date: Thu, 10 Oct 2024 13:21:31 +0200 Subject: [PATCH] fix: do no rerun the each block when array change from empty to empty (#13553) * do no rerun the each block when array change from empty to empty * rename empty yo was_empty * add test * fix nullable array on SSR * format * rewrite * chore: add changeset --------- Co-authored-by: Paolo Ricciuti --- .changeset/loud-walls-wave.md | 5 ++ .../src/internal/client/dom/blocks/each.js | 9 +++ packages/svelte/src/internal/server/index.js | 9 ++- .../samples/each-was-empty/_config.js | 80 +++++++++++++++++++ .../samples/each-was-empty/main.svelte | 22 +++++ 5 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 .changeset/loud-walls-wave.md create mode 100644 packages/svelte/tests/runtime-runes/samples/each-was-empty/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/each-was-empty/main.svelte diff --git a/.changeset/loud-walls-wave.md b/.changeset/loud-walls-wave.md new file mode 100644 index 0000000000..4293d07085 --- /dev/null +++ b/.changeset/loud-walls-wave.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: do no rerun the each block when array change from empty to empty diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index 922fdb09cb..55bb474db4 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -132,6 +132,8 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f /** @type {Effect | null} */ var fallback = null; + var was_empty = false; + block(() => { var collection = get_collection(); @@ -143,6 +145,13 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f var length = array.length; + if (was_empty && length === 0) { + // ignore updates if the array is empty, + // and it already was empty on previous run + return; + } + was_empty = length === 0; + /** `true` if there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */ let mismatch = false; diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 69f897123e..c1cbe32a60 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -507,9 +507,12 @@ export { await_block as await }; /** @param {any} array_like_or_iterator */ export function ensure_array_like(array_like_or_iterator) { - return array_like_or_iterator?.length !== undefined - ? array_like_or_iterator - : Array.from(array_like_or_iterator); + if (array_like_or_iterator) { + return array_like_or_iterator.length !== undefined + ? array_like_or_iterator + : Array.from(array_like_or_iterator); + } + return []; } /** diff --git a/packages/svelte/tests/runtime-runes/samples/each-was-empty/_config.js b/packages/svelte/tests/runtime-runes/samples/each-was-empty/_config.js new file mode 100644 index 0000000000..8595e97953 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/each-was-empty/_config.js @@ -0,0 +1,80 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +// https://github.com/sveltejs/svelte/issues/13550 +// https://github.com/sveltejs/svelte/pull/13553 +export default test({ + html: ``, + + async test({ assert, target }) { + const [increment, set_undefined, set_null, set_empty, set_list] = + target.querySelectorAll('button'); + + let [span] = target.querySelectorAll('span'); + + // initial value + assert.exists(span); + assert.equal(span.innerHTML, '0'); + + // increment value + flushSync(() => increment.click()); + assert.equal(span.innerHTML, '1'); + + // change collection to undefined + flushSync(() => set_undefined.click()); + // increment value + flushSync(() => increment.click()); + assert.equal(span.innerHTML, '2'); + + // change collection to null + flushSync(() => set_null.click()); + // increment value + flushSync(() => increment.click()); + assert.equal(span.innerHTML, '3'); + + // change collection to empty + flushSync(() => set_empty.click()); + // increment value + flushSync(() => increment.click()); + assert.equal(span.innerHTML, '4'); + + // change collection to undefined + flushSync(() => set_undefined.click()); + // increment value + flushSync(() => increment.click()); + assert.equal(span.innerHTML, '5'); + + // change collection to [1,2,3] + flushSync(() => set_list.click()); + [span] = target.querySelectorAll('span'); + assert.notExists(span); + assert.equal(target.querySelectorAll('li').length, 3); + + // change collection to undefined + flushSync(() => set_undefined.click()); + [span] = target.querySelectorAll('span'); + assert.exists(span); + assert.equal(span.innerHTML, '5'); + + // increment value + flushSync(() => increment.click()); + assert.equal(span.innerHTML, '6'); + + // change collection to null + flushSync(() => set_null.click()); + // increment value + flushSync(() => increment.click()); + assert.equal(span.innerHTML, '7'); + + // change collection to empty + flushSync(() => set_empty.click()); + // increment value + flushSync(() => increment.click()); + assert.equal(span.innerHTML, '8'); + + assert.htmlEqual( + target.innerHTML, + `` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/each-was-empty/main.svelte b/packages/svelte/tests/runtime-runes/samples/each-was-empty/main.svelte new file mode 100644 index 0000000000..d5a917045c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/each-was-empty/main.svelte @@ -0,0 +1,22 @@ + + + + + + + + + \ No newline at end of file