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
pull/16909/head
Simon H 2 days ago committed by GitHub
parent 1b1f144396
commit 78481862fb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: hydrate each blocks inside element correctly

@ -99,7 +99,14 @@ export function process_children(nodes, initial, is_element, context) {
if (is_static_element(node, context.state)) { if (is_static_element(node, context.state)) {
skipped += 1; skipped += 1;
} else if (node.type === 'EachBlock' && nodes.length === 1 && is_element) { } else if (
node.type === 'EachBlock' &&
nodes.length === 1 &&
is_element &&
// 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
// TODO switch this around and instead optimize for elements with a single block child and not require extra comments (neither for async nor normally)
!(node.body.metadata.has_await || node.metadata.expression.has_await)
) {
node.metadata.is_controlled = true; node.metadata.is_controlled = true;
} else { } else {
const id = flush_node(false, node.type === 'RegularElement' ? node.name : 'node'); const id = flush_node(false, node.type === 'RegularElement' ? node.name : 'node');

@ -0,0 +1,16 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
mode: ['async-server', 'hydrate', 'client'],
ssrHtml: `<ul><li>1</li></ul> <button>add</button>`,
async test({ assert, target }) {
await tick();
const [add] = target.querySelectorAll('button');
add.click();
await tick();
assert.htmlEqual(target.innerHTML, `<ul><li>1</li><li>2</li></ul> <button>add</button>`);
}
});

@ -0,0 +1,11 @@
<script>
let array = $state(Promise.resolve([1]));
</script>
<ul>
{#each await array as item}
<li>{item}</li>
{/each}
</ul>
<button onclick={() => array = Promise.resolve([1, 2])}>add</button>
Loading…
Cancel
Save