Skip to content

Commit 128c325

Browse files
committed
fix: remove source onchange from proxy on reassignment
1 parent 2a3fb7a commit 128c325

File tree

3 files changed

+70
-1
lines changed

3 files changed

+70
-1
lines changed

packages/svelte/src/internal/client/reactivity/sources.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ import {
2727
UNOWNED,
2828
MAYBE_DIRTY,
2929
BLOCK_EFFECT,
30-
ROOT_EFFECT
30+
ROOT_EFFECT,
31+
PROXY_ONCHANGE_SYMBOL
3132
} from '../constants.js';
3233
import * as e from '../errors.js';
3334
import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js';
@@ -239,6 +240,14 @@ export function internal_set(source, value) {
239240
if (!source.equals(value)) {
240241
var old_value = source.v;
241242

243+
if (old_value != null && source.o?.onchange) {
244+
// @ts-ignore
245+
const remove = old_value[PROXY_ONCHANGE_SYMBOL];
246+
if (remove && typeof remove === 'function') {
247+
remove(source.o?.onchange, true);
248+
}
249+
}
250+
242251
if (is_destroying_effect) {
243252
old_values.set(source, value);
244253
} else {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
async test({ assert, target, logs }) {
6+
const [btn, btn2, btn3, btn4] = target.querySelectorAll('button');
7+
8+
flushSync(() => {
9+
btn.click();
10+
});
11+
assert.deepEqual(logs, ['a']);
12+
13+
flushSync(() => {
14+
btn2.click();
15+
});
16+
assert.deepEqual(logs, ['a', 'b', 'c']);
17+
flushSync(() => {
18+
btn3.click();
19+
});
20+
assert.deepEqual(logs, ['a', 'b', 'c', 'b', 'c']);
21+
flushSync(() => {
22+
btn4.click();
23+
});
24+
assert.deepEqual(logs, ['a', 'b', 'c', 'b', 'c', 'c']);
25+
flushSync(() => {
26+
btn2.click();
27+
});
28+
assert.deepEqual(logs, ['a', 'b', 'c', 'b', 'c', 'c', 'b']);
29+
}
30+
});
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<script>
2+
let obj = { count: 0 };
3+
4+
let a = $state(obj, {
5+
onchange() {
6+
console.log('a');
7+
}
8+
});
9+
10+
let b = $state(obj, {
11+
onchange() {
12+
console.log('b');
13+
}
14+
});
15+
16+
let c = $state(b, {
17+
onchange() {
18+
console.log('c');
19+
}
20+
});
21+
</script>
22+
23+
<button onclick={()=> a.count++}>{a.count}</button>
24+
<button onclick={()=> b.count++}>{b.count}</button>
25+
<button onclick={()=> c.count++}>{c.count}</button>
26+
27+
<!-- click this button, then click the b and c buttons. in theory
28+
you should see either 'b changed' or 'c changed', but
29+
instead clicking b causes both -->
30+
<button onclick={() => c = { count: c.count }}>unlink</button>

0 commit comments

Comments
 (0)