diff --git a/.changeset/fuzzy-spies-love.md b/.changeset/fuzzy-spies-love.md new file mode 100644 index 000000000000..9129d986bc71 --- /dev/null +++ b/.changeset/fuzzy-spies-love.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: preserve old dependencies when updating reaction inside fork diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 2075081f96ad..70eeabb78935 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -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; } diff --git a/packages/svelte/tests/runtime-runes/samples/fork-derived-dependency-rollback/_config.js b/packages/svelte/tests/runtime-runes/samples/fork-derived-dependency-rollback/_config.js new file mode 100644 index 000000000000..c5db69f7d473 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/fork-derived-dependency-rollback/_config.js @@ -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'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/fork-derived-dependency-rollback/main.svelte b/packages/svelte/tests/runtime-runes/samples/fork-derived-dependency-rollback/main.svelte new file mode 100644 index 000000000000..800c82b56b3b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/fork-derived-dependency-rollback/main.svelte @@ -0,0 +1,27 @@ + + + +{#if count} +{/if} + + + + + + + +

{count}