fix: improve bind:this support around proxyied state (#10732)

* fix: improve bind:this support around proxyied state

* fix: improve bind:this support around proxyied state

* fix: improve bind:this support around proxyied state
pull/10716/head
Dominic Gannaway 10 months ago committed by GitHub
parent 304db0d5b2
commit 7c00f1dacb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: improve bind:this support around proxyied state

@ -77,6 +77,7 @@ import { run } from '../common.js';
import { bind_transition, trigger_transitions } from './transitions.js'; import { bind_transition, trigger_transitions } from './transitions.js';
import { mutable_source, source, set } from './reactivity/sources.js'; import { mutable_source, source, set } from './reactivity/sources.js';
import { safe_equal, safe_not_equal } from './reactivity/equality.js'; import { safe_equal, safe_not_equal } from './reactivity/equality.js';
import { STATE_SYMBOL } from './constants.js';
/** @type {Set<string>} */ /** @type {Set<string>} */
const all_registerd_events = new 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 {Element} element_or_component
* @param {(value: unknown, ...parts: unknown[]) => void} update * @param {(value: unknown, ...parts: unknown[]) => void} update
@ -1360,7 +1372,7 @@ export function bind_this(element_or_component, update, get_value, get_parts) {
update(element_or_component, ...parts); update(element_or_component, ...parts);
// If this is an effect rerun (cause: each block context changes), then nullfiy the binding at // 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. // 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); 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. // 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. // This solves the case where one variable is shared across multiple this-bindings.
effect(() => { effect(() => {
if (get_value(...parts) === element_or_component) { if (parts && is_bound_this(get_value(...parts), element_or_component)) {
update(null, ...parts); update(null, ...parts);
} }
}); });

@ -0,0 +1,4 @@
<script>
export const a = {};
</script>
<div>Hello world</div>

@ -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: `<button>Toggle</button><div>Hello\nworld</div>`,
async test({ assert, target }) {
const [btn1] = target.querySelectorAll('button');
flushSync(() => {
btn1?.click();
});
assert.htmlEqual(target.innerHTML, `<button>Toggle</button>`);
flushSync(() => {
btn1?.click();
});
assert.htmlEqual(target.innerHTML, `<button>Toggle</button><div>Hello\nworld</div>`);
assert.deepEqual(log, [{ a: {} }, null, { a: {} }]);
}
});

@ -0,0 +1,2 @@
/** @type {any[]} */
export const log = [];

@ -0,0 +1,23 @@
<script>
import { log } from './log.js';
import Component from './Component.svelte';
let type = $state(Component)
let elem = $state()
$effect(() => {
log.push(elem);
});
</script>
<button onclick={() => {
if (!type) {
type = Component
} else {
type = false
}
}}>Toggle</button>
<svelte:component bind:this={elem} this={type}>
Content
</svelte:component>
Loading…
Cancel
Save