eager-bind-this-teardown
Rich Harris 5 months ago
parent 2a1f5ada13
commit e813bdf1f7

@ -1,5 +1,7 @@
import { STATE_SYMBOL } from '#client/constants';
import { capture_signals } from '../../../legacy.js';
import { effect, render_effect } from '../../../reactivity/effects.js';
import { old_values } from '../../../reactivity/sources.js';
import { untrack } from '../../../runtime.js';
import { queue_micro_task } from '../../task.js';
@ -48,12 +50,16 @@ export function bind_this(element_or_component = {}, update, get_value, get_part
});
return () => {
// We cannot use effects in the teardown phase, we we use a microtask instead.
queue_micro_task(() => {
if (parts && is_bound_this(get_value(...parts), element_or_component)) {
update(null, ...parts);
}
});
const signals = capture_signals(get_value);
if (parts && is_bound_this(get_value(...parts), element_or_component)) {
update(null, ...parts);
}
if (signals && signals.size === 1) {
const [signal] = [...signals];
old_values.set(signal, element_or_component);
}
};
});

@ -13,7 +13,7 @@ export let captured_signals = null;
* @template T
* @param {() => T} fn
*/
function capture_signals(fn) {
export function capture_signals(fn) {
var previous_captured_signals = captured_signals;
try {

@ -0,0 +1,9 @@
<script>
let { data } = $props();
const processed = $derived(data.toUpperCase());
export function getProcessed() {
return processed;
}
</script>

@ -0,0 +1,15 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
ssrHtml: '<button>clear</button><p></p>',
html: '<button>clear</button><p>HELLO</p>',
async test({ assert, target }) {
const [button] = target.querySelectorAll('button');
flushSync(() => button.click());
assert.htmlEqual(target.innerHTML, '<button>clear</button><p></p>');
}
});

@ -0,0 +1,19 @@
<script>
import Inner from './Inner.svelte';
let value = $state('hello');
let innerComp = $state();
// Reads Inner's derived value from outside the {#if} block, keeping it
// connected in the reactive graph even after the branch is destroyed.
const externalView = $derived(innerComp?.getProcessed() ?? '');
</script>
{#if value}
{@const result = value}
<Inner data={result} bind:this={innerComp} />
{/if}
<button onclick={() => (value = undefined)}>clear</button>
<p>{externalView}</p>

@ -0,0 +1,18 @@
import { flushSync, tick } from 'svelte';
import { test } from '../../test';
export default test({
html: '<button>clear</button><div><p>hello</p></div>',
async test({ assert, target, raf, logs }) {
const [button] = target.querySelectorAll('button');
flushSync(() => button.click());
assert.deepEqual(logs, ['hello']);
// Let the transition finish and clean up
raf.tick(100);
assert.htmlEqual(target.innerHTML, '<button>clear</button>');
}
});

@ -0,0 +1,32 @@
<script>
let value = $state('hello');
function compute(v) {
console.log(v);
return { data: v, ready: true };
}
function fade(node) {
return {
duration: 100,
tick: (t) => {
node.style.opacity = String(t);
}
};
}
</script>
<button
onclick={() => {
value = undefined;
}}>clear</button
>
{#if value}
{@const result = compute(value)}
{#if result.ready}
<div out:fade|global>
<p>{result.data}</p>
</div>
{/if}
{/if}
Loading…
Cancel
Save