fix: ensure toStore root effect is connected to correct parent effect (#15574)

* fix: ensure toStore root effect is connected to correct parent effect

* prettier

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/15575/head
Dominic Gannaway 6 months ago committed by GitHub
parent ade66c6fea
commit 1f37c02f91
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: ensure toStore root effect is connected to correct parent effect

@ -6,6 +6,12 @@ import {
} from '../internal/client/reactivity/effects.js';
import { get, writable } from './shared/index.js';
import { createSubscriber } from '../reactivity/create-subscriber.js';
import {
active_effect,
active_reaction,
set_active_effect,
set_active_reaction
} from '../internal/client/runtime.js';
export { derived, get, readable, readonly, writable } from './shared/index.js';
@ -39,19 +45,34 @@ export { derived, get, readable, readonly, writable } from './shared/index.js';
* @returns {Writable<V> | Readable<V>}
*/
export function toStore(get, set) {
let init_value = get();
var effect = active_effect;
var reaction = active_reaction;
var init_value = get();
const store = writable(init_value, (set) => {
// If the value has changed before we call subscribe, then
// we need to treat the value as already having run
let ran = init_value !== get();
var ran = init_value !== get();
// TODO do we need a different implementation on the server?
const teardown = effect_root(() => {
render_effect(() => {
const value = get();
if (ran) set(value);
var teardown;
// Apply the reaction and effect at the time of toStore being called
var previous_reaction = active_reaction;
var previous_effect = active_effect;
set_active_reaction(reaction);
set_active_effect(effect);
try {
teardown = effect_root(() => {
render_effect(() => {
const value = get();
if (ran) set(value);
});
});
});
} finally {
set_active_reaction(previous_reaction);
set_active_effect(previous_effect);
}
ran = true;

@ -0,0 +1,16 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target }) {
let btn = target.querySelector('button');
btn?.click();
flushSync();
assert.htmlEqual(
target.innerHTML,
`<div>Count 1!</div><div>Count from store 1!</div><button>Add 1</button>`
);
}
});

@ -0,0 +1,11 @@
<script>
import { toStore } from "svelte/store";
let counter = $state(0);
const count = toStore(() => counter, value => counter = value);
</script>
<div>Count {counter}!</div>
<div>Count from store {$count}!</div>
<button onclick={() => counter+=1}>Add 1</button>
Loading…
Cancel
Save