fix: better handle array property deletion reactivity (#9921)

pull/9929/head
Dominic Gannaway 1 year ago committed by GitHub
parent b779e72eb6
commit 59c7487f36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: better handle array property deletion reactivity

@ -140,13 +140,28 @@ const handler = {
deleteProperty(target, prop) { deleteProperty(target, prop) {
const metadata = target[STATE_SYMBOL]; const metadata = target[STATE_SYMBOL];
const s = metadata.s.get(prop); 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 (s !== undefined) set(s, UNINITIALIZED);
if (prop in target) update(metadata.v); if (prop in target) update(metadata.v);
return delete target[prop]; return boolean;
}, },
get(target, prop, receiver) { get(target, prop, receiver) {

@ -0,0 +1,52 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
html: `<button>push</button><button>pop</button><p>1</p><p>2</p><p>3</p>`,
async test({ assert, target }) {
const [btn1, btn2] = target.querySelectorAll('button');
flushSync(() => {
btn1.click();
});
assert.htmlEqual(
target.innerHTML,
`<button>push</button><button>pop</button><p>1</p><p>2</p><p>3</p><p>4</p>`
);
flushSync(() => {
btn1.click();
});
assert.htmlEqual(
target.innerHTML,
`<button>push</button><button>pop</button><p>1</p><p>2</p><p>3</p><p>4</p><p>5</p>`
);
flushSync(() => {
btn2.click();
});
assert.htmlEqual(
target.innerHTML,
`<button>push</button><button>pop</button><p>1</p><p>2</p><p>3</p><p>4</p>`
);
flushSync(() => {
btn2.click();
});
assert.htmlEqual(
target.innerHTML,
`<button>push</button><button>pop</button><p>1</p><p>2</p><p>3</p>`
);
flushSync(() => {
btn2.click();
});
assert.htmlEqual(target.innerHTML, `<button>push</button><button>pop</button><p>1</p><p>2</p>`);
}
});

@ -0,0 +1,15 @@
<script>
let numbers = $state([{id: 1}, {id: 2}, {id: 3}]);
</script>
<button onclick={() => numbers.push({id: numbers.length + 1})}>
push
</button>
<button onclick={() => numbers.pop()}>
pop
</button>
{#each numbers as number}
<p>{number.id}</p>
{/each}
Loading…
Cancel
Save