Skip to content

Commit 4b8674f

Browse files
authored
fix: ensure async initial store value is noticed (#12486)
* fix: ensure async initial store value is noticed fixes #12341 * fix another case
1 parent ea22840 commit 4b8674f

File tree

6 files changed

+57
-15
lines changed

6 files changed

+57
-15
lines changed

.changeset/nasty-carrots-develop.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 async initial store value is noticed

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,7 @@ export function set(source, value) {
9797
// reactions as we only allocate and assign the reactions after the signal
9898
// has fully executed. So in the case of ensuring it registers the reaction
9999
// properly for itself, we need to ensure the current effect actually gets
100-
// scheduled. i.e:
101-
//
102-
// $effect(() => x++)
103-
//
104-
// We additionally want to skip this logic when initialising store sources
100+
// scheduled. i.e: `$effect(() => x++)`
105101
if (
106102
is_runes() &&
107103
current_effect !== null &&

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,19 @@ export function store_get(store, store_name, stores) {
3131
set(entry.source, undefined);
3232
entry.unsubscribe = noop;
3333
} else {
34-
var initial = true;
34+
var is_synchronous_callback = true;
3535

3636
entry.unsubscribe = subscribe_to_store(store, (v) => {
37-
if (initial) {
38-
// if the first time the store value is read is inside a derived,
39-
// we will hit the `state_unsafe_mutation` error if we `set` the value
37+
if (is_synchronous_callback) {
38+
// If the first updates to the store value (possibly multiple of them) are synchronously
39+
// inside a derived, we will hit the `state_unsafe_mutation` error if we `set` the value
4040
entry.source.v = v;
41-
initial = false;
4241
} else {
4342
set(entry.source, v);
4443
}
4544
});
45+
46+
is_synchronous_callback = false;
4647
}
4748
}
4849

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
mode: ['client'],
5+
async test({ assert, target }) {
6+
assert.htmlEqual(target.innerHTML, ' / ');
7+
await new Promise((r) => setTimeout(r, 110));
8+
assert.htmlEqual(target.innerHTML, '42 / 42');
9+
}
10+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<script>
2+
function store() {
3+
return {
4+
subscribe: (cb) => {
5+
setTimeout(() => {
6+
cb(42);
7+
}, 100);
8+
9+
return () => {};
10+
}
11+
};
12+
}
13+
14+
const value1 = store();
15+
const value2 = store();
16+
const derivedValue = $derived($value1);
17+
</script>
18+
19+
{$value2} / {derivedValue}
Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
1-
<script>
2-
import { writable } from "svelte/store";
3-
let store = writable('store');
4-
let name = $derived($store); // store signal is updated during reading this, which normally errors, but shouldn't for stores
1+
<script>
2+
import { writable } from 'svelte/store';
3+
4+
let store1 = writable('store');
5+
let store2 = {
6+
subscribe: (cb) => {
7+
cb('...');
8+
cb('Hello');
9+
return () => {};
10+
}
11+
};
12+
13+
// store signal is updated during reading this, which normally errors, but shouldn't for stores
14+
let name = $derived($store1);
15+
let hello = $derived($store2);
516
</script>
617

7-
<h1>Hello {name}</h1>
18+
<h1>{hello} {name}</h1>

0 commit comments

Comments
 (0)