Skip to content

Commit 056b201

Browse files
paoloricciutidummdidummRich-Harris
authored
fix: don't set derived values during time traveling (#17163)
* chore: failing test for derived + fork + block * fix: don't set derived values during time travel * fix: skip no async * fix: only set `derived.v` outside a fork * chore: simplify Co-authored-by: Simon H <[email protected]> * tidy up test a bit (missing text makes things confusing in the sandbox) * update comment * tweak --------- Co-authored-by: Simon H <[email protected]> Co-authored-by: Rich Harris <[email protected]>
1 parent 9ccbd73 commit 056b201

File tree

4 files changed

+53
-10
lines changed

4 files changed

+53
-10
lines changed

packages/svelte/src/internal/client/reactivity/deriveds.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -356,9 +356,14 @@ export function update_derived(derived) {
356356
var value = execute_derived(derived);
357357

358358
if (!derived.equals(value)) {
359-
// TODO can we avoid setting `derived.v` when `batch_values !== null`,
360-
// without causing the value to be stale later?
361-
derived.v = value;
359+
// in a fork, we don't update the underlying value, just `batch_values`.
360+
// the underlying value will be updated when the fork is committed.
361+
// otherwise, the next time we get here after a 'real world' state
362+
// change, `derived.equals` may incorrectly return `true`
363+
if (!current_batch?.is_fork) {
364+
derived.v = value;
365+
}
366+
362367
derived.wv = increment_write_version();
363368
}
364369

@@ -374,7 +379,7 @@ export function update_derived(derived) {
374379
// only cache the value if we're in a tracking context, otherwise we won't
375380
// clear the cache in `mark_reactions` when dependencies are updated
376381
if (effect_tracking()) {
377-
batch_values.set(derived, derived.v);
382+
batch_values.set(derived, value);
378383
}
379384
} else {
380385
var status = (derived.f & CONNECTED) === 0 ? MAYBE_DIRTY : CLEAN;

packages/svelte/src/internal/client/runtime.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -611,21 +611,19 @@ export function get(signal) {
611611

612612
return value;
613613
}
614-
} else if (is_derived) {
614+
} else if (is_derived && !batch_values?.has(signal)) {
615615
derived = /** @type {Derived} */ (signal);
616616

617-
if (batch_values?.has(derived)) {
618-
return batch_values.get(derived);
619-
}
620-
621617
if (is_dirty(derived)) {
622618
update_derived(derived);
623619
}
624620

625621
if (is_updating_effect && effect_tracking() && (derived.f & CONNECTED) === 0) {
626622
reconnect(derived);
627623
}
628-
} else if (batch_values?.has(signal)) {
624+
}
625+
626+
if (batch_values?.has(signal)) {
629627
return batch_values.get(signal);
630628
}
631629

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
skip_no_async: true,
6+
async test({ assert, target }) {
7+
const [fork, update] = target.querySelectorAll('button');
8+
9+
flushSync(() => {
10+
fork.click();
11+
});
12+
flushSync(() => {
13+
update.click();
14+
});
15+
16+
const p = target.querySelector('p');
17+
18+
assert.equal(p?.textContent, 'one');
19+
}
20+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script>
2+
import { fork } from "svelte";
3+
4+
let state = $state(0);
5+
let count = $derived(state);
6+
</script>
7+
8+
<button onclick={() => {
9+
fork(() => {
10+
state++;
11+
});
12+
}}>fork</button>
13+
14+
<button onclick={() => {
15+
state++;
16+
}}>update</button>
17+
18+
{#if count === 1}
19+
<p>one</p>
20+
{/if}

0 commit comments

Comments
 (0)