Skip to content

Commit

Permalink
fix: do no rerun the each block when array change from empty to empty (
Browse files Browse the repository at this point in the history
…#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>
  • Loading branch information
adiguba and paoloricciuti authored Oct 10, 2024
1 parent 6776947 commit 531ff62
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/loud-walls-wave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"svelte": patch
---

fix: do no rerun the each block when array change from empty to empty
9 changes: 9 additions & 0 deletions packages/svelte/src/internal/client/dom/blocks/each.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();

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

Expand Down
9 changes: 6 additions & 3 deletions packages/svelte/src/internal/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 [];
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -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>`
);
}
});
Original file line number Diff line number Diff line change
@@ -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>

0 comments on commit 531ff62

Please sign in to comment.