fix: ensure more bindings run without active context

Continuation of #14194, fixes #15742
binding-evt-without-context
Simon Holthausen 5 months ago
parent bfb969a6cc
commit d34b1be9be

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: ensure more bindings run without active context

@ -12,10 +12,14 @@ import { add_form_reset_listener } from '../misc.js';
* then listens to the given events until the render effect context is destroyed * then listens to the given events until the render effect context is destroyed
* @param {EventTarget} target * @param {EventTarget} target
* @param {Array<string>} events * @param {Array<string>} events
* @param {(event?: Event) => void} handler * @param {(event?: Event) => void} event_handler
* @param {any} call_handler_immediately * @param {any} call_handler_immediately
*/ */
export function listen(target, events, handler, call_handler_immediately = true) { export function listen(target, events, event_handler, call_handler_immediately = true) {
// Just like user-defined events, our internal events shouldn't have any reactive context
/** @type {typeof event_handler} */
const handler = (e) => without_reactive_context(() => event_handler(e));
if (call_handler_immediately) { if (call_handler_immediately) {
handler(); handler();
} }
@ -32,6 +36,9 @@ export function listen(target, events, handler, call_handler_immediately = true)
} }
/** /**
* Runs a function without a reactive context.
* This is important for events which should "start fresh" and not inherit
* context that accidentally happens to be active at the time of the event.
* @template T * @template T
* @param {() => T} fn * @param {() => T} fn
*/ */

@ -62,5 +62,5 @@ export function bind_window_scroll(type, get, set = get) {
* @param {(size: number) => void} set * @param {(size: number) => void} set
*/ */
export function bind_window_size(type, set) { export function bind_window_size(type, set) {
listen(window, ['resize'], () => without_reactive_context(() => set(window[type]))); listen(window, ['resize'], () => set(window[type]));
} }

@ -2,6 +2,6 @@ import { test } from '../../test';
export default test({ export default test({
test({ assert, logs }) { test({ assert, logs }) {
assert.deepEqual(logs, [false]); assert.deepEqual(logs, ['bind:activeElement false', 'bind:value false']);
} }
}); });

@ -1,24 +1,29 @@
<script> <script>
let bar = $state(''); let bar = $state('');
const foo = { const value = {
set bar(v) { set value(v) {
console.log('bind:value ' + $effect.tracking());
console.log($effect.tracking());
bar = v; bar = v;
}, },
get bar() { get value() {
return bar; return bar;
} }
};
const active = {
set bar(_v) {
console.log('bind:activeElement ' + $effect.tracking());
} }
};
let input; let input;
$effect(() => { $effect(() => {
input.value = 'everybody'; input.value = 'everybody';
input.dispatchEvent(new window.Event('input')); input.dispatchEvent(new window.Event('input'));
}) });
</script> </script>
<input type="text" bind:value={foo.bar} bind:this={input}> <svelte:document bind:activeElement={active.bar} />
<input type="text" bind:value={value.value} bind:this={input}>

Loading…
Cancel
Save