Skip to content

Commit 52a36bd

Browse files
fix: link offscreen items and last effect in each block correctly (alternative) (#17244)
It's possible that due to how new elements are inserted into the array that `effect.last` is wrong. We need to ensure it is really the last item to keep items properly connected to the graph. Fixes #17201 --------- Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
1 parent f2dd477 commit 52a36bd

File tree

4 files changed

+80
-0
lines changed

4 files changed

+80
-0
lines changed

.changeset/great-ghosts-unite.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: link offscreen items and last effect in each block correctly

packages/svelte/src/internal/client/dom/blocks/each.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,10 @@ function link(state, prev, next) {
637637
state.first = next;
638638
state.effect.first = next && next.e;
639639
} else {
640+
if (prev.e === state.effect.last && next !== null) {
641+
state.effect.last = next.e;
642+
}
643+
640644
if (prev.e.next) {
641645
prev.e.next.prev = null;
642646
}
@@ -648,6 +652,10 @@ function link(state, prev, next) {
648652
if (next === null) {
649653
state.effect.last = prev && prev.e;
650654
} else {
655+
if (next.e === state.effect.last && prev === null) {
656+
state.effect.last = next.e.prev;
657+
}
658+
651659
if (next.e.prev) {
652660
next.e.prev.next = null;
653661
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
async test({ assert, target }) {
6+
const [add, adjust] = target.querySelectorAll('button');
7+
8+
add.click();
9+
flushSync();
10+
assert.htmlEqual(
11+
target.innerHTML,
12+
`<button>add</button> <button>adjust</button>
13+
<h2>Keyed</h2>
14+
<div>Item: 1. Index: 0</div>
15+
<div>Item: 0. Index: 1</div>
16+
<h2>Unkeyed</h2>
17+
<div>Item: 1. Index: 0</div>
18+
<div>Item: 0. Index: 1</div>`
19+
);
20+
21+
add.click();
22+
flushSync();
23+
assert.htmlEqual(
24+
target.innerHTML,
25+
`<button>add</button> <button>adjust</button>
26+
<h2>Keyed</h2>
27+
<div>Item: 2. Index: 0</div>
28+
<div>Item: 1. Index: 1</div>
29+
<div>Item: 0. Index: 2</div>
30+
<h2>Unkeyed</h2>
31+
<div>Item: 2. Index: 0</div>
32+
<div>Item: 1. Index: 1</div>
33+
<div>Item: 0. Index: 2</div>`
34+
);
35+
36+
adjust.click();
37+
flushSync();
38+
assert.htmlEqual(
39+
target.innerHTML,
40+
`<button>add</button> <button>adjust</button>
41+
<h2>Keyed</h2>
42+
<div>Item: 2. Index: 0</div>
43+
<div>Item: 1. Index: 1</div>
44+
<div>Item: 10. Index: 2</div>
45+
<h2>Unkeyed</h2>
46+
<div>Item: 2. Index: 0</div>
47+
<div>Item: 1. Index: 1</div>
48+
<div>Item: 10. Index: 2</div>`
49+
);
50+
}
51+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script>
2+
const items = $state([{ t: 0 }]);
3+
</script>
4+
5+
<button onclick={() => items.unshift({t:items.length})}>add</button>
6+
<button onclick={() => items.at(-1).t = 10}>adjust</button>
7+
8+
<h2>Keyed</h2>
9+
{#each items as item, index (item)}
10+
<div>Item: {item.t}. Index: {index}</div>
11+
{/each}
12+
13+
<h2>Unkeyed</h2>
14+
{#each items as item, index}
15+
<div>Item: {item.t}. Index: {index}</div>
16+
{/each}

0 commit comments

Comments
 (0)