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
* @param {EventTarget} target
* @param {Array<string>} events
* @param {(event?: Event) => void} handler
* @param {(event?: Event) => void} event_handler
* @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) {
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
* @param {() => T} fn
*/

@ -62,5 +62,5 @@ export function bind_window_scroll(type, get, set = get) {
* @param {(size: number) => void} 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({
test({ assert, logs }) {
assert.deepEqual(logs, [false]);
assert.deepEqual(logs, ['bind:activeElement false', 'bind:value false']);
}
});

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