fix: preserve old dependencies when updating reaction inside fork (#17579)

Co-authored-by: David Roizenman <hmnd@users.noreply.github.com>
pull/17564/head
Rich Harris 4 weeks ago committed by GitHub
parent ece2e83eb9
commit 3608b3c869
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: preserve old dependencies when updating reaction inside fork

@ -43,7 +43,13 @@ import {
set_dev_current_component_function,
set_dev_stack
} from './context.js';
import { Batch, batch_values, flushSync, schedule_effect } from './reactivity/batch.js';
import {
Batch,
batch_values,
current_batch,
flushSync,
schedule_effect
} from './reactivity/batch.js';
import { handle_error } from './error-handling.js';
import { UNINITIALIZED } from '../../constants.js';
import { captured_signals } from './legacy.js';
@ -249,10 +255,16 @@ export function update_reaction(reaction) {
var result = fn();
var deps = reaction.deps;
// Don't remove reactions during fork;
// they must remain for when fork is discarded
var is_fork = current_batch?.is_fork;
if (new_deps !== null) {
var i;
remove_reactions(reaction, skipped_deps);
if (!is_fork) {
remove_reactions(reaction, skipped_deps);
}
if (deps !== null && skipped_deps > 0) {
deps.length = skipped_deps + new_deps.length;
@ -268,7 +280,7 @@ export function update_reaction(reaction) {
(deps[i].reactions ??= []).push(reaction);
}
}
} else if (deps !== null && skipped_deps < deps.length) {
} else if (!is_fork && deps !== null && skipped_deps < deps.length) {
remove_reactions(reaction, skipped_deps);
deps.length = skipped_deps;
}

@ -0,0 +1,25 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
skip_no_async: true,
async test({ assert, target }) {
const [fork_btn, _toggle_btn, inc_count_1_btn] = target.querySelectorAll('button');
const p = /** @type {HTMLElement} */ (target.querySelector('p'));
assert.equal(p.textContent, '0');
// Trigger derived to re-evaluate during fork and switch to tracking count_2
flushSync(() => {
fork_btn.click();
});
assert.equal(p.textContent, '0');
flushSync(() => {
inc_count_1_btn.click();
});
assert.equal(p.textContent, '1');
}
});

@ -0,0 +1,27 @@
<script>
import { fork } from 'svelte';
let show_count_1 = $state(true);
let count_1 = $state(0);
let count_2 = $state(0);
const count = $derived(show_count_1 ? count_1 : count_2);
</script>
<!-- This if block causes the derived to execute during the fork, switching its dependency
away from count_1 and over to count_2. After discard, count_1 should still be tracked. -->
{#if count}
{/if}
<button onclick={() => {
const f = fork(() => {
show_count_1 = !show_count_1;
});
f.discard();
}}>fork toggle</button>
<button onclick={() => show_count_1 = !show_count_1}>toggle</button>
<button onclick={() => count_1++}>increment count 1</button>
<button onclick={() => count_2++}>increment count 2</button>
<p>{count}</p>
Loading…
Cancel
Save