Skip to content

Commit 1f37c02

Browse files
trueadmRich-Harris
andauthored
fix: ensure toStore root effect is connected to correct parent effect (sveltejs#15574)
* fix: ensure toStore root effect is connected to correct parent effect * prettier --------- Co-authored-by: Rich Harris <[email protected]>
1 parent ade66c6 commit 1f37c02

File tree

4 files changed

+60
-7
lines changed

4 files changed

+60
-7
lines changed

.changeset/twelve-bananas-destroy.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: ensure toStore root effect is connected to correct parent effect

packages/svelte/src/store/index-client.js

+28-7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ import {
66
} from '../internal/client/reactivity/effects.js';
77
import { get, writable } from './shared/index.js';
88
import { createSubscriber } from '../reactivity/create-subscriber.js';
9+
import {
10+
active_effect,
11+
active_reaction,
12+
set_active_effect,
13+
set_active_reaction
14+
} from '../internal/client/runtime.js';
915

1016
export { derived, get, readable, readonly, writable } from './shared/index.js';
1117

@@ -39,19 +45,34 @@ export { derived, get, readable, readonly, writable } from './shared/index.js';
3945
* @returns {Writable<V> | Readable<V>}
4046
*/
4147
export function toStore(get, set) {
42-
let init_value = get();
48+
var effect = active_effect;
49+
var reaction = active_reaction;
50+
var init_value = get();
51+
4352
const store = writable(init_value, (set) => {
4453
// If the value has changed before we call subscribe, then
4554
// we need to treat the value as already having run
46-
let ran = init_value !== get();
55+
var ran = init_value !== get();
4756

4857
// TODO do we need a different implementation on the server?
49-
const teardown = effect_root(() => {
50-
render_effect(() => {
51-
const value = get();
52-
if (ran) set(value);
58+
var teardown;
59+
// Apply the reaction and effect at the time of toStore being called
60+
var previous_reaction = active_reaction;
61+
var previous_effect = active_effect;
62+
set_active_reaction(reaction);
63+
set_active_effect(effect);
64+
65+
try {
66+
teardown = effect_root(() => {
67+
render_effect(() => {
68+
const value = get();
69+
if (ran) set(value);
70+
});
5371
});
54-
});
72+
} finally {
73+
set_active_reaction(previous_reaction);
74+
set_active_effect(previous_effect);
75+
}
5576

5677
ran = true;
5778

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
async test({ assert, target }) {
6+
let btn = target.querySelector('button');
7+
8+
btn?.click();
9+
flushSync();
10+
11+
assert.htmlEqual(
12+
target.innerHTML,
13+
`<div>Count 1!</div><div>Count from store 1!</div><button>Add 1</button>`
14+
);
15+
}
16+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>
2+
import { toStore } from "svelte/store";
3+
4+
let counter = $state(0);
5+
const count = toStore(() => counter, value => counter = value);
6+
</script>
7+
8+
<div>Count {counter}!</div>
9+
<div>Count from store {$count}!</div>
10+
11+
<button onclick={() => counter+=1}>Add 1</button>

0 commit comments

Comments
 (0)