fix: always synchronously call `bind:this` (#12679)

fixes #12673

#12591 wrongfully applied the "wrap in effect if an action on this element" logic for `bind:this`
pull/12680/head
Simon H 3 months ago committed by GitHub
parent ccccac394b
commit 01e7845180
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: always synchronously call `bind:this`

@ -3164,16 +3164,15 @@ export const template_visitors = {
}
const parent = /** @type {import('#compiler').SvelteNode} */ (context.path.at(-1));
const has_action_directive =
parent.type === 'RegularElement' && parent.attributes.find((a) => a.type === 'UseDirective');
// Bindings need to happen after attribute updates, therefore after the render effect, and in order with events/actions.
// bind:this is a special case as it's one-way and could influence the render effect.
if (node.name === 'this') {
state.init.push(
b.stmt(has_action_directive ? b.call('$.effect', b.thunk(call_expr)) : call_expr)
);
state.init.push(b.stmt(call_expr));
} else {
const has_action_directive =
parent.type === 'RegularElement' &&
parent.attributes.find((a) => a.type === 'UseDirective');
state.after_update.push(
b.stmt(has_action_directive ? b.call('$.effect', b.thunk(call_expr)) : call_expr)
);

@ -22,6 +22,7 @@ export default test({
}
assert.deepEqual(value, [
'bind:this true',
'1',
'2',
'3',

@ -1,29 +1,86 @@
<script>
import { onMount } from 'svelte';
export let value = [];
function one(elem) { elem.addEventListener('input', () => { value.push('1'); }); }
function four(elem) { elem.addEventListener('input', () => { value.push('4'); }); }
function eight(elem) { elem.addEventListener('input', () => { value.push('8'); }); }
function twelve(elem) { elem.addEventListener('input', () => { value.push('12'); }); }
function fifteen(elem) { elem.addEventListener('input', () => { value.push('15'); }); }
function seventeen(elem) { elem.addEventListener('input', () => { value.push('17'); }); }
function one(elem) {
elem.addEventListener('input', () => {
value.push('1');
});
}
function four(elem) {
elem.addEventListener('input', () => {
value.push('4');
});
}
function eight(elem) {
elem.addEventListener('input', () => {
value.push('8');
});
}
function twelve(elem) {
elem.addEventListener('input', () => {
value.push('12');
});
}
function fifteen(elem) {
elem.addEventListener('input', () => {
value.push('15');
});
}
function seventeen(elem) {
elem.addEventListener('input', () => {
value.push('17');
});
}
const foo = {
set two(v) { value.push('2'); },
set six(v) { value.push('6'); },
set nine(v) { value.push('9'); },
set eleven(v) { value.push('11'); },
set thirteen(v) { value.push('13'); },
set sixteen(v) { value.push('16'); },
set two(v) {
value.push('2');
},
set six(v) {
value.push('6');
},
set nine(v) {
value.push('9');
},
set eleven(v) {
value.push('11');
},
set thirteen(v) {
value.push('13');
},
set sixteen(v) {
value.push('16');
}
};
function three() {
value.push('3');
}
function five() {
value.push('5');
}
function seven() {
value.push('7');
}
function ten() {
value.push('10');
}
function fourteen() {
value.push('14');
}
function eighteen() {
value.push('18');
}
function three() { value.push('3'); }
function five() { value.push('5'); }
function seven() { value.push('7'); }
function ten() { value.push('10'); }
function fourteen() { value.push('14'); }
function eighteen() { value.push('18'); }
let el;
onMount(() => {
// ensure that bind:this doesn't influence the order of directives
// and isn't affected itself by an action being on the element
value.push('bind:this ' + !!el);
});
</script>
<input use:one bind:value={foo.two} on:input={three} />
@ -31,4 +88,4 @@
<input on:input={seven} use:eight bind:value={foo.nine} />
<input on:input={ten} bind:value={foo.eleven} use:twelve />
<input bind:value={foo.thirteen} on:input={fourteen} use:fifteen />
<input bind:value={foo.sixteen} use:seventeen on:input={eighteen} />
<input bind:this={el} bind:value={foo.sixteen} use:seventeen on:input={eighteen} />

Loading…
Cancel
Save