fix: remove source onchange from proxy on reassignment

pull/15579/head
paoloricciuti 6 months ago
parent 2a3fb7a308
commit 128c3254db

@ -27,7 +27,8 @@ import {
UNOWNED,
MAYBE_DIRTY,
BLOCK_EFFECT,
ROOT_EFFECT
ROOT_EFFECT,
PROXY_ONCHANGE_SYMBOL
} from '../constants.js';
import * as e from '../errors.js';
import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js';
@ -239,6 +240,14 @@ export function internal_set(source, value) {
if (!source.equals(value)) {
var old_value = source.v;
if (old_value != null && source.o?.onchange) {
// @ts-ignore
const remove = old_value[PROXY_ONCHANGE_SYMBOL];
if (remove && typeof remove === 'function') {
remove(source.o?.onchange, true);
}
}
if (is_destroying_effect) {
old_values.set(source, value);
} else {

@ -0,0 +1,30 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target, logs }) {
const [btn, btn2, btn3, btn4] = target.querySelectorAll('button');
flushSync(() => {
btn.click();
});
assert.deepEqual(logs, ['a']);
flushSync(() => {
btn2.click();
});
assert.deepEqual(logs, ['a', 'b', 'c']);
flushSync(() => {
btn3.click();
});
assert.deepEqual(logs, ['a', 'b', 'c', 'b', 'c']);
flushSync(() => {
btn4.click();
});
assert.deepEqual(logs, ['a', 'b', 'c', 'b', 'c', 'c']);
flushSync(() => {
btn2.click();
});
assert.deepEqual(logs, ['a', 'b', 'c', 'b', 'c', 'c', 'b']);
}
});

@ -0,0 +1,30 @@
<script>
let obj = { count: 0 };
let a = $state(obj, {
onchange() {
console.log('a');
}
});
let b = $state(obj, {
onchange() {
console.log('b');
}
});
let c = $state(b, {
onchange() {
console.log('c');
}
});
</script>
<button onclick={()=> a.count++}>{a.count}</button>
<button onclick={()=> b.count++}>{b.count}</button>
<button onclick={()=> c.count++}>{c.count}</button>
<!-- click this button, then click the b and c buttons. in theory
you should see either 'b changed' or 'c changed', but
instead clicking b causes both -->
<button onclick={() => c = { count: c.count }}>unlink</button>
Loading…
Cancel
Save