diff --git a/.changeset/lovely-carpets-lick.md b/.changeset/lovely-carpets-lick.md new file mode 100644 index 0000000000..bd86e2ef44 --- /dev/null +++ b/.changeset/lovely-carpets-lick.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: better handle array property deletion reactivity diff --git a/packages/svelte/src/internal/client/proxy/proxy.js b/packages/svelte/src/internal/client/proxy/proxy.js index 57c2178798..ae993267d6 100644 --- a/packages/svelte/src/internal/client/proxy/proxy.js +++ b/packages/svelte/src/internal/client/proxy/proxy.js @@ -140,13 +140,28 @@ const handler = { deleteProperty(target, prop) { const metadata = target[STATE_SYMBOL]; - const s = metadata.s.get(prop); + const is_array = metadata.a; + const boolean = delete target[prop]; + + // If we have mutated an array directly, and the deletion + // was successful we will also need to update the length + // before updating the field or the version. This is to + // ensure any effects observing length can execute before + // effects that listen to the fields – otherwise they will + // operate an an index that no longer exists. + if (is_array && boolean) { + const ls = metadata.s.get('length'); + const length = target.length - 1; + if (ls !== undefined && ls.v !== length) { + set(ls, length); + } + } if (s !== undefined) set(s, UNINITIALIZED); if (prop in target) update(metadata.v); - return delete target[prop]; + return boolean; }, get(target, prop, receiver) { diff --git a/packages/svelte/tests/runtime-runes/samples/each-mutation-2/_config.js b/packages/svelte/tests/runtime-runes/samples/each-mutation-2/_config.js new file mode 100644 index 0000000000..34ffe2ee14 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/each-mutation-2/_config.js @@ -0,0 +1,52 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: `
1
2
3
`, + + async test({ assert, target }) { + const [btn1, btn2] = target.querySelectorAll('button'); + + flushSync(() => { + btn1.click(); + }); + + assert.htmlEqual( + target.innerHTML, + `1
2
3
4
` + ); + + flushSync(() => { + btn1.click(); + }); + + assert.htmlEqual( + target.innerHTML, + `1
2
3
4
5
` + ); + + flushSync(() => { + btn2.click(); + }); + + assert.htmlEqual( + target.innerHTML, + `1
2
3
4
` + ); + + flushSync(() => { + btn2.click(); + }); + + assert.htmlEqual( + target.innerHTML, + `1
2
3
` + ); + + flushSync(() => { + btn2.click(); + }); + + assert.htmlEqual(target.innerHTML, `1
2
`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/each-mutation-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/each-mutation-2/main.svelte new file mode 100644 index 0000000000..530532ccf2 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/each-mutation-2/main.svelte @@ -0,0 +1,15 @@ + + + + + + +{#each numbers as number} +{number.id}
+{/each}