fix: correctly assign bind:this with multiples (#9617)

* fix: correctly assign bind:this with multiples

* better fix

* better fix

* lint

* lint

* Update packages/svelte/src/internal/client/render.js

---------

Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
pull/9621/head
Dominic Gannaway 1 year ago committed by GitHub
parent c22ebffb00
commit 509f92d29e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: correct bind this multiple bindings

@ -891,6 +891,7 @@ function serialize_inline_component(node, component_name, context) {
if (bind_this !== null) {
const prev = fn;
const assignment = b.assignment('=', bind_this, b.id('$$value'));
const bind_this_id = bind_this;
fn = (node_id) =>
b.call(
'$.bind_this',
@ -898,7 +899,8 @@ function serialize_inline_component(node, component_name, context) {
b.arrow(
[b.id('$$value')],
serialize_set_binding(assignment, context, () => context.visit(assignment))
)
),
bind_this_id
);
}
@ -2620,7 +2622,7 @@ export const template_visitors = {
}
case 'this':
call_expr = b.call(`$.bind_this`, state.node, setter);
call_expr = b.call(`$.bind_this`, state.node, setter, node.expression);
break;
case 'textContent':

@ -2,7 +2,6 @@ import { DEV } from 'esm-env';
import {
append_child,
child,
child_frag,
clone_node,
create_element,
init_operations,
@ -61,7 +60,8 @@ import {
push,
current_component_context,
pop,
schedule_task
schedule_task,
managed_render_effect
} from './runtime.js';
import {
current_hydration_fragment,
@ -1225,14 +1225,21 @@ export function bind_prop(props, prop, value) {
/**
* @param {Element} element_or_component
* @param {(value: unknown) => void} update
* @param {import('./types.js').MaybeSignal} binding
* @returns {void}
*/
export function bind_this(element_or_component, update) {
export function bind_this(element_or_component, update, binding) {
untrack(() => {
update(element_or_component);
render_effect(() => () => {
untrack(() => {
update(null);
// 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.
render_effect(() => {
untrack(() => {
if (!is_signal(binding) || binding.v === element_or_component) {
update(null);
}
});
});
});
});

@ -0,0 +1,25 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
test({ assert, component, target }) {
const [b1, b2, b3] = target.querySelectorAll('button');
const first_h1 = target.querySelector('h1');
assert.deepEqual(component.log, [undefined, first_h1]);
flushSync(() => {
b3.click();
});
const third_h1 = target.querySelector('h1');
assert.deepEqual(component.log, [undefined, first_h1, third_h1]);
flushSync(() => {
b1.click();
});
assert.deepEqual(component.log, [undefined, first_h1, third_h1, target.querySelector('h1')]);
}
});

@ -0,0 +1,27 @@
<script>
let activeTab = 0;
let activeHeading;
export let log = [];
$: log.push(activeHeading);
</script>
<div class="tabs">
<div class="tab-toggles">
<button class:active={activeTab === 0} on:click={() => activeTab = 0}>Tab 1</button>
<button class:active={activeTab === 1} on:click={() => activeTab = 1}>Tab 2</button>
<button class:active={activeTab === 2} on:click={() => activeTab = 2}>Tab 3</button>
</div>
<div class="tab-content">
{#if activeTab === 0}
<div><h1 bind:this={activeHeading}>Tab 1</h1></div>
{/if}
{#if activeTab === 1}
<div><h1 bind:this={activeHeading}>Tab 2</h1></div>
{/if}
{#if activeTab === 2}
<div><h1 bind:this={activeHeading}>Tab 3</h1></div>
{/if}
</div>
<duiv>
</div>
Loading…
Cancel
Save