Skip to content

Commit 7848186

Browse files
authored
fix: hydrate each blocks inside element correctly (#16908)
We have an each block optimization that omits the comment when the each block is the sole child of an element. This optimization clashes with async which wants to skip ahead to the sibling closing comment. For now we therefore remove that optimization when the each block is async. In the long run we should instead optimize _all_ cases where _any_ block is the sole child of an element, in both async and sync mode, consistently. fixes #16905 fixes #16907
1 parent 1b1f144 commit 7848186

File tree

4 files changed

+40
-1
lines changed

4 files changed

+40
-1
lines changed

.changeset/chilly-bats-build.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: hydrate each blocks inside element correctly

packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,14 @@ export function process_children(nodes, initial, is_element, context) {
9999

100100
if (is_static_element(node, context.state)) {
101101
skipped += 1;
102-
} else if (node.type === 'EachBlock' && nodes.length === 1 && is_element) {
102+
} else if (
103+
node.type === 'EachBlock' &&
104+
nodes.length === 1 &&
105+
is_element &&
106+
// In case it's wrapped in async the async logic will want to skip sibling nodes up until the end, hence we cannot make this controlled
107+
// TODO switch this around and instead optimize for elements with a single block child and not require extra comments (neither for async nor normally)
108+
!(node.body.metadata.has_await || node.metadata.expression.has_await)
109+
) {
103110
node.metadata.is_controlled = true;
104111
} else {
105112
const id = flush_node(false, node.type === 'RegularElement' ? node.name : 'node');
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { tick } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
mode: ['async-server', 'hydrate', 'client'],
6+
ssrHtml: `<ul><li>1</li></ul> <button>add</button>`,
7+
8+
async test({ assert, target }) {
9+
await tick();
10+
const [add] = target.querySelectorAll('button');
11+
12+
add.click();
13+
await tick();
14+
assert.htmlEqual(target.innerHTML, `<ul><li>1</li><li>2</li></ul> <button>add</button>`);
15+
}
16+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>
2+
let array = $state(Promise.resolve([1]));
3+
</script>
4+
5+
<ul>
6+
{#each await array as item}
7+
<li>{item}</li>
8+
{/each}
9+
</ul>
10+
11+
<button onclick={() => array = Promise.resolve([1, 2])}>add</button>

0 commit comments

Comments
 (0)