pull/18815/merge
Aayush Dani 2 days ago committed by GitHub
commit e1c21da8f5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: preserve component updates when a derived cleans up an effect that was already unlinked

@ -601,6 +601,9 @@ export function unlink_effect(effect) {
if (parent.first === effect) parent.first = next;
if (parent.last === effect) parent.last = prev;
}
// A derived can still reference this effect and destroy it after it has been unlinked.
effect.prev = effect.next = null;
}
/**

@ -0,0 +1,16 @@
<script>
let { item, attachHandle, onIncrement } = $props();
</script>
<div>
<span>{item.id}: {item.value}</span>
{#if item.id === 'a'}
<span>Type A</span>
{:else}
<span>Type B</span>
{/if}
<button {@attach attachHandle}>drag</button>
<button onclick={onIncrement}>+1</button>
</div>

@ -0,0 +1,48 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target, logs }) {
const buttons = target.querySelectorAll('button');
const values = target.querySelectorAll('div > span:first-child');
const second = values[1];
assert.deepEqual(logs.splice(0), ['setup a', 'setup b']);
for (let value = 1; value <= 3; value += 1) {
buttons[1].click();
await tick();
assert.equal(values[0].textContent, `a: ${value}`);
assert.equal(second.textContent, 'b: 0');
}
assert.deepEqual(logs.splice(0), [
'cleanup a',
'setup a',
'cleanup a',
'setup a',
'cleanup a',
'setup a'
]);
buttons[3].click();
await tick();
assert.equal(second.textContent, 'b: 1');
assert.deepEqual(logs.splice(0), ['cleanup b', 'setup b']);
buttons[4].click();
await tick();
assert.equal(target.querySelector('div > span:first-child'), second);
assert.deepEqual(logs.splice(0), ['cleanup a']);
buttons[3].click();
await tick();
assert.equal(second.textContent, 'b: 2');
assert.deepEqual(logs.splice(0), ['cleanup b', 'setup b']);
buttons[4].click();
await tick();
assert.equal(target.querySelector('div'), null);
assert.deepEqual(logs.splice(0), ['cleanup b']);
}
});

@ -0,0 +1,15 @@
export function create_attachment(input) {
$effect(() => () => console.log(`cleanup ${input.id}`));
// This effect has no reactive dependencies or cleanup.
$effect(() => console.log(`setup ${input.id}`));
$effect.pre(() => {
void input.index;
});
$effect(() => {
void input.index;
});
return { attach: () => () => {} };
}

@ -0,0 +1,31 @@
<script>
import Item from './Item.svelte';
import { create_attachment } from './attachment.svelte.js';
let items = $state([
{ id: 'a', value: 0 },
{ id: 'b', value: 0 }
]);
function increment(id) {
items = items.map((item) => (item.id === id ? { id: item.id, value: item.value + 1 } : item));
}
</script>
<pre>{JSON.stringify(items)}</pre>
{#each items as item, index (item.id)}
{@const attachment = create_attachment({
id: item.id,
get index() {
return index;
}
})}
<Item
item={items[index]}
attachHandle={attachment.attach}
onIncrement={() => increment(item.id)}
/>
{/each}
<button onclick={() => (items = items.slice(1))}>Remove first</button>
Loading…
Cancel
Save