From 79e90afb8838ca3e3f12d9e4ec4e80011db8caa6 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Fri, 8 Mar 2024 16:00:25 +0000 Subject: [PATCH 1/3] fix: improve bind:this support around proxyied state --- .changeset/blue-ants-raise.md | 5 ++++ packages/svelte/src/internal/client/render.js | 18 ++++++++++-- .../samples/bind-this-proxy/Component.svelte | 4 +++ .../samples/bind-this-proxy/_config.js | 29 +++++++++++++++++++ .../samples/bind-this-proxy/log.js | 2 ++ .../samples/bind-this-proxy/main.svelte | 23 +++++++++++++++ 6 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 .changeset/blue-ants-raise.md create mode 100644 packages/svelte/tests/runtime-runes/samples/bind-this-proxy/Component.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/bind-this-proxy/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/bind-this-proxy/log.js create mode 100644 packages/svelte/tests/runtime-runes/samples/bind-this-proxy/main.svelte diff --git a/.changeset/blue-ants-raise.md b/.changeset/blue-ants-raise.md new file mode 100644 index 000000000000..b77e70216ddb --- /dev/null +++ b/.changeset/blue-ants-raise.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: improve bind:this support around proxyied state diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index 0fc26d3e3483..296d094dda03 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -77,6 +77,7 @@ import { run } from '../common.js'; import { bind_transition, trigger_transitions } from './transitions.js'; import { mutable_source, source, set } from './reactivity/sources.js'; import { safe_equal, safe_not_equal } from './reactivity/equality.js'; +import { STATE_SYMBOL } from './constants.js'; /** @type {Set} */ const all_registerd_events = new Set(); @@ -1336,6 +1337,17 @@ export function bind_prop(props, prop, value) { } } +/** + * @param {any} bound_value + * @param {Element} element_or_component + * @returns {boolean} + */ +function is_bound_this(bound_value, element_or_component) { + // Find the original target if the value is proxied. + const proxy_target = bound_value && bound_value[STATE_SYMBOL]?.t; + return bound_value === element_or_component || (proxy_target === element_or_component); +} + /** * @param {Element} element_or_component * @param {(value: unknown, ...parts: unknown[]) => void} update @@ -1353,14 +1365,14 @@ export function bind_this(element_or_component, update, get_value, get_parts) { const e = effect(() => { old_parts = parts; // We only track changes to the parts, not the value itself to avoid unnecessary reruns. - parts = get_parts?.() || []; + parts = get_parts?.() || (element_or_component ? [element_or_component] : []); untrack(() => { if (element_or_component !== get_value(...parts)) { update(element_or_component, ...parts); // If this is an effect rerun (cause: each block context changes), then nullfiy the binding at // the previous position if it isn't already taken over by a different effect. - if (old_parts && get_value(...old_parts) === element_or_component) { + if (old_parts && is_bound_this(get_value(...old_parts), element_or_component)) { update(null, ...old_parts); } } @@ -1374,7 +1386,7 @@ export function bind_this(element_or_component, update, get_value, get_parts) { // Defer to the next tick so that all updates can be reconciled first. // This solves the case where one variable is shared across multiple this-bindings. effect(() => { - if (get_value(...parts) === element_or_component) { + if (parts && is_bound_this(get_value(...parts), element_or_component)) { update(null, ...parts); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/bind-this-proxy/Component.svelte b/packages/svelte/tests/runtime-runes/samples/bind-this-proxy/Component.svelte new file mode 100644 index 000000000000..4e3f2d6c0a99 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/bind-this-proxy/Component.svelte @@ -0,0 +1,4 @@ + +
Hello world
diff --git a/packages/svelte/tests/runtime-runes/samples/bind-this-proxy/_config.js b/packages/svelte/tests/runtime-runes/samples/bind-this-proxy/_config.js new file mode 100644 index 000000000000..e075b5a5888d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/bind-this-proxy/_config.js @@ -0,0 +1,29 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; +import { log } from './log.js'; + +export default test({ + before_test: () => { + log.length = 0; + }, + + html: `
Hello\nworld
`, + + async test({ assert, target }) { + const [btn1] = target.querySelectorAll('button'); + + flushSync(() => { + btn1?.click(); + }); + + assert.htmlEqual(target.innerHTML, ``); + + flushSync(() => { + btn1?.click(); + }); + + assert.htmlEqual(target.innerHTML, `
Hello\nworld
`); + + assert.deepEqual(log, [{ a: {} }, null, { a: {} }]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/bind-this-proxy/log.js b/packages/svelte/tests/runtime-runes/samples/bind-this-proxy/log.js new file mode 100644 index 000000000000..d3df521f4da7 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/bind-this-proxy/log.js @@ -0,0 +1,2 @@ +/** @type {any[]} */ +export const log = []; diff --git a/packages/svelte/tests/runtime-runes/samples/bind-this-proxy/main.svelte b/packages/svelte/tests/runtime-runes/samples/bind-this-proxy/main.svelte new file mode 100644 index 000000000000..adc67f46aee5 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/bind-this-proxy/main.svelte @@ -0,0 +1,23 @@ + + + + + + Content + From 277dd65b2065f5e8c8f30a66b453b06501c909ba Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Fri, 8 Mar 2024 16:04:18 +0000 Subject: [PATCH 2/3] fix: improve bind:this support around proxyied state --- packages/svelte/src/internal/client/render.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index 296d094dda03..b0e32b46cb57 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -1345,7 +1345,7 @@ export function bind_prop(props, prop, value) { function is_bound_this(bound_value, element_or_component) { // Find the original target if the value is proxied. const proxy_target = bound_value && bound_value[STATE_SYMBOL]?.t; - return bound_value === element_or_component || (proxy_target === element_or_component); + return bound_value === element_or_component || proxy_target === element_or_component; } /** From 589258575d118b1894a5eb8473f63f377504e1a8 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Fri, 8 Mar 2024 16:14:15 +0000 Subject: [PATCH 3/3] fix: improve bind:this support around proxyied state --- packages/svelte/src/internal/client/render.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index b0e32b46cb57..75c361d7a9ef 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -1365,7 +1365,7 @@ export function bind_this(element_or_component, update, get_value, get_parts) { const e = effect(() => { old_parts = parts; // We only track changes to the parts, not the value itself to avoid unnecessary reruns. - parts = get_parts?.() || (element_or_component ? [element_or_component] : []); + parts = get_parts?.() || []; untrack(() => { if (element_or_component !== get_value(...parts)) {