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 <ricciutipaolo@gmail.com>
pull/13559/head
adiGuba 3 months ago committed by GitHub
parent 6776947ae8
commit 531ff6243c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: do no rerun the each block when array change from empty to empty

@ -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;

@ -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 [];
}
/**

@ -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: `<button>clicks: 0</button><button>undefined</button><button>null</button><button>empty</button><button>[1,2,3]</button><ul><li>count = <span>0</span></li></ul>`,
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,
`<button>clicks: 8</button><button>undefined</button><button>null</button><button>empty</button><button>[1,2,3]</button><ul><li>count = <span>8</span></li></ul>`
);
}
});

@ -0,0 +1,22 @@
<script>
let list = $state()
let count = $state(0);
function increment() {
count += 1;
}
</script>
<button onclick={increment}>clicks: {count}</button>
<button onclick={()=>list=undefined}>undefined</button>
<button onclick={()=>list=null}>null</button>
<button onclick={()=>list=[]}>empty</button>
<button onclick={()=>list=[1,2,3]}>[1,2,3]</button>
<ul>
{#each list as a}
<li>item : {a}</li>
{:else}
<li>count = <span>{count}</span></li>
{/each}
</ul>
Loading…
Cancel
Save