diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 070230a461..39e02be764 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -356,9 +356,14 @@ export function update_derived(derived) { var value = execute_derived(derived); if (!derived.equals(value)) { - // TODO can we avoid setting `derived.v` when `batch_values !== null`, - // without causing the value to be stale later? - derived.v = value; + // in a fork, we don't update the underlying value, just `batch_values`. + // 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) { + derived.v = value; + } + derived.wv = increment_write_version(); } @@ -374,7 +379,7 @@ export function update_derived(derived) { // only cache the value if we're in a tracking context, otherwise we won't // clear the cache in `mark_reactions` when dependencies are updated if (effect_tracking()) { - batch_values.set(derived, derived.v); + batch_values.set(derived, value); } } else { var status = (derived.f & CONNECTED) === 0 ? MAYBE_DIRTY : CLEAN; diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 258f6962fa..5ece0d79b6 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -611,13 +611,9 @@ export function get(signal) { return value; } - } else if (is_derived) { + } else if (is_derived && !batch_values?.has(signal)) { derived = /** @type {Derived} */ (signal); - if (batch_values?.has(derived)) { - return batch_values.get(derived); - } - if (is_dirty(derived)) { update_derived(derived); } @@ -625,7 +621,9 @@ export function get(signal) { if (is_updating_effect && effect_tracking() && (derived.f & CONNECTED) === 0) { reconnect(derived); } - } else if (batch_values?.has(signal)) { + } + + if (batch_values?.has(signal)) { return batch_values.get(signal); } diff --git a/packages/svelte/tests/runtime-runes/samples/fork-derived-value/_config.js b/packages/svelte/tests/runtime-runes/samples/fork-derived-value/_config.js new file mode 100644 index 0000000000..0635db7501 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/fork-derived-value/_config.js @@ -0,0 +1,20 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + skip_no_async: true, + async test({ assert, target }) { + const [fork, update] = target.querySelectorAll('button'); + + flushSync(() => { + fork.click(); + }); + flushSync(() => { + update.click(); + }); + + const p = target.querySelector('p'); + + assert.equal(p?.textContent, 'one'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/fork-derived-value/main.svelte b/packages/svelte/tests/runtime-runes/samples/fork-derived-value/main.svelte new file mode 100644 index 0000000000..06e0f1f264 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/fork-derived-value/main.svelte @@ -0,0 +1,20 @@ + + + + + + +{#if count === 1} +
one
+{/if}