From 128c3254db9e35b30b49bf39d5df5387900bb7cb Mon Sep 17 00:00:00 2001 From: paoloricciuti Date: Mon, 17 Mar 2025 23:02:39 +0100 Subject: [PATCH] fix: remove source onchange from proxy on reassignment --- .../src/internal/client/reactivity/sources.js | 11 ++++++- .../state-onchange-reassign-proxy/_config.js | 30 +++++++++++++++++++ .../state-onchange-reassign-proxy/main.svelte | 30 +++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 packages/svelte/tests/runtime-runes/samples/state-onchange-reassign-proxy/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/state-onchange-reassign-proxy/main.svelte diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index c2512d0d40..6fe837ff12 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -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 { diff --git a/packages/svelte/tests/runtime-runes/samples/state-onchange-reassign-proxy/_config.js b/packages/svelte/tests/runtime-runes/samples/state-onchange-reassign-proxy/_config.js new file mode 100644 index 0000000000..92ddd1369d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/state-onchange-reassign-proxy/_config.js @@ -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']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/state-onchange-reassign-proxy/main.svelte b/packages/svelte/tests/runtime-runes/samples/state-onchange-reassign-proxy/main.svelte new file mode 100644 index 0000000000..d1931cbaac --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/state-onchange-reassign-proxy/main.svelte @@ -0,0 +1,30 @@ + + + + + + + + \ No newline at end of file