fix: give effect teardowns the value from before the first write in a flush (#18620)

Fixes #18619

Make sure to not override old values with newer values that are part of the same flush

---------

Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
pull/18606/merge
Yuuki Oohara 3 weeks ago committed by GitHub
parent 221dcae8ca
commit a224dd5867
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: give effect teardowns the value from before the first write in a flush

@ -420,6 +420,7 @@ export class Batch {
}
if (next_batch !== null) {
old_values.clear();
next_batch.#process();
}
}

@ -180,7 +180,13 @@ export function set(source, value, should_proxy = false) {
*/
export function internal_set(source, value, updated_during_traversal = null) {
if (!source.equals(value)) {
old_values.set(source, is_destroying_effect ? value : source.v);
if (is_destroying_effect) {
old_values.set(source, value);
} else if (!old_values.has(source)) {
// only record the value from before the first write in this flush, otherwise a
// teardown would see the value from before whichever write happened to be last
old_values.set(source, source.v);
}
var batch = Batch.ensure();
batch.capture(source, value);

@ -0,0 +1,17 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target, logs }) {
const button = target.querySelector('button');
flushSync(() => button?.click());
assert.deepEqual(logs, [
'register: one',
'unregister: one',
'leftover: none',
'register: three'
]);
}
});

@ -0,0 +1,21 @@
<script>
let value = $state('one');
const registry = new Set();
$effect(() => {
registry.add(value);
console.log(`register: ${value}`);
return () => {
registry.delete(value);
console.log(`unregister: ${value}`);
console.log(`leftover: ${[...registry].join(', ') || 'none'}`);
};
});
</script>
<button onclick={() => {
value = 'two';
value = 'three';
}}>go</button>
Loading…
Cancel
Save