diff --git a/.changeset/strong-berries-fry.md b/.changeset/strong-berries-fry.md new file mode 100644 index 0000000000..60dbb290a8 --- /dev/null +++ b/.changeset/strong-berries-fry.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: store forked derived values diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 22526df7c1..0c2ba81f0c 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -68,6 +68,14 @@ export let previous_batch = null; */ export let batch_values = null; +/** + * When time travelling (i.e. working in one batch, while other batches + * still have ongoing work), we ignore the real values of affected + * signals in favour of their values within the batch + * @type {Map | null} + */ +export let forked_derived_values = null; + // TODO this should really be a property of `batch` /** @type {Effect[]} */ let queued_root_effects = []; @@ -962,8 +970,12 @@ export function fork(fn) { var committed = false; var settled = batch.settled(); + forked_derived_values = new Map(); + flushSync(fn); + forked_derived_values = null; + // revert state changes for (var [source, value] of batch.previous) { source.v = value; diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 39e02be764..693a86fcf0 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -34,7 +34,7 @@ import { async_mode_flag, tracing_mode_flag } from '../../flags/index.js'; import { Boundary } from '../dom/blocks/boundary.js'; import { component_context } from '../context.js'; import { UNINITIALIZED } from '../../../constants.js'; -import { batch_values, current_batch } from './batch.js'; +import { batch_values, current_batch, forked_derived_values } from './batch.js'; import { unset_context } from './async.js'; import { deferred } from '../../shared/utils.js'; @@ -360,8 +360,10 @@ export function update_derived(derived) { // the underlying value will be updated when the fork is committed. // otherwise, the next time we get here after a 'real world' state // change, `derived.equals` may incorrectly return `true` - if (!current_batch?.is_fork) { + if (!forked_derived_values) { derived.v = value; + } else { + forked_derived_values.set(derived, value); } derived.wv = increment_write_version(); diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 5ece0d79b6..4e53b41728 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -43,7 +43,13 @@ import { set_dev_stack } from './context.js'; import * as w from './warnings.js'; -import { Batch, batch_values, flushSync, schedule_effect } from './reactivity/batch.js'; +import { + Batch, + batch_values, + flushSync, + forked_derived_values, + schedule_effect +} from './reactivity/batch.js'; import { handle_error } from './error-handling.js'; import { UNINITIALIZED } from '../../constants.js'; import { captured_signals } from './legacy.js'; @@ -621,6 +627,10 @@ export function get(signal) { if (is_updating_effect && effect_tracking() && (derived.f & CONNECTED) === 0) { reconnect(derived); } + + if (forked_derived_values?.has(derived)) { + return forked_derived_values.get(derived); + } } if (batch_values?.has(signal)) { diff --git a/packages/svelte/tests/runtime-runes/samples/fork-derived-value-immediate/_config.js b/packages/svelte/tests/runtime-runes/samples/fork-derived-value-immediate/_config.js new file mode 100644 index 0000000000..52478687e5 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/fork-derived-value-immediate/_config.js @@ -0,0 +1,15 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + skip_no_async: true, + async test({ assert, target, logs }) { + const fork = target.querySelector('button'); + + flushSync(() => { + fork?.click(); + }); + + assert.deepEqual(logs, [1]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/fork-derived-value-immediate/main.svelte b/packages/svelte/tests/runtime-runes/samples/fork-derived-value-immediate/main.svelte new file mode 100644 index 0000000000..fe3fa26195 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/fork-derived-value-immediate/main.svelte @@ -0,0 +1,13 @@ + + +