Skip to content

Commit eea8a18

Browse files
authored
fix: ensure fork always accesses correct values (#17098)
Not all batches will flush right after being activated, some will be activated and then `get` is called on a signal. In that case the value was wrong because we did not apply the changes of that batch. By doing `this.apply()` during `activate()` we ensure we do, which fixes (among other things, likely) a forking bug where old values where sneaking in. Fixes #17079
1 parent b7625fd commit eea8a18

File tree

5 files changed

+51
-4
lines changed

5 files changed

+51
-4
lines changed

.changeset/eight-news-laugh.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: ensure fork always accesses correct values

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import {
1515
DERIVED,
1616
BOUNDARY_EFFECT,
1717
EAGER_EFFECT,
18-
HEAD_EFFECT
18+
HEAD_EFFECT,
19+
ERROR_VALUE
1920
} from '#client/constants';
2021
import { async_mode_flag } from '../../flags/index.js';
2122
import { deferred, define_property } from '../../shared/utils.js';
@@ -285,12 +286,16 @@ export class Batch {
285286
this.previous.set(source, value);
286287
}
287288

288-
this.current.set(source, source.v);
289-
batch_values?.set(source, source.v);
289+
// Don't save errors in `batch_values`, or they won't be thrown in `runtime.js#get`
290+
if ((source.f & ERROR_VALUE) === 0) {
291+
this.current.set(source, source.v);
292+
batch_values?.set(source, source.v);
293+
}
290294
}
291295

292296
activate() {
293297
current_batch = this;
298+
this.apply();
294299
}
295300

296301
deactivate() {
@@ -492,7 +497,7 @@ export class Batch {
492497
}
493498

494499
apply() {
495-
if (!async_mode_flag || batches.size === 1) return;
500+
if (!async_mode_flag || (!this.is_fork && batches.size === 1)) return;
496501

497502
// if there are multiple batches, we are 'time travelling' —
498503
// we need to override values with the ones in this batch...
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<script>
2+
let { x } = $props();
3+
console.log(x);
4+
await Promise.resolve();
5+
console.log(x);
6+
</script>
7+
8+
{x}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
async test({ assert, target, logs }) {
5+
const btn = target.querySelector('button');
6+
7+
btn?.click();
8+
await new Promise((r) => setTimeout(r, 2));
9+
assert.htmlEqual(target.innerHTML, `<button>fork</button> universe`);
10+
assert.deepEqual(logs, ['universe', 'universe']);
11+
}
12+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script>
2+
import { fork } from 'svelte';
3+
import Child from './Child.svelte';
4+
let x = $state('world');
5+
</script>
6+
7+
<button onclick={async () => {
8+
const f = fork(() => {
9+
x = 'universe'
10+
});
11+
await new Promise(r => setTimeout(r));
12+
f.commit();
13+
}}>fork</button>
14+
15+
{#if x === 'universe'}
16+
<Child {x} />
17+
{/if}

0 commit comments

Comments
 (0)