diff --git a/.changeset/proud-dots-swim.md b/.changeset/proud-dots-swim.md new file mode 100644 index 0000000000..3f86f115ae --- /dev/null +++ b/.changeset/proud-dots-swim.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: invoke `$state.link` callback at the correct time diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index d0c6441040..d6cc28c5c7 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -27,6 +27,7 @@ import { } from '../constants.js'; import * as e from '../errors.js'; import { derived } from './deriveds.js'; +import { render_effect } from './effects.js'; let inspect_effects = new Set(); @@ -53,44 +54,23 @@ export function source(v) { * @returns {(value?: V) => V} */ export function source_link(get_value, callback) { - var was_local = false; - var init = false; - var local_source = source(/** @type {V} */ (undefined)); + var s = source(/** @type {V} */ (undefined)); + var ran = false; - var linked_derived = derived(() => { - var local_value = /** @type {V} */ (get(local_source)); - var linked_value = get_value(); + callback ??= (value) => set(s, value); - if (was_local) { - was_local = false; - return local_value; - } - - return linked_value; - }); - - return function (/** @type {any} */ value) { - if (arguments.length > 0) { - was_local = true; - set(local_source, value); - get(linked_derived); - return value; - } - - var linked_value = get(linked_derived); - - if (init) { - if (callback !== undefined) { - untrack(() => callback(linked_value)); - return local_source.v; - } + render_effect(() => { + if (ran) { + callback(get_value()); } else { - init = true; + s.v = get_value(); } + }); - local_source.v = linked_value; + ran = true; - return linked_value; + return function (/** @type {any} */ value) { + return arguments.length === 1 ? set(s, /** @type {V} */ (value)) : get(s); }; } diff --git a/packages/svelte/tests/runtime-runes/samples/state-link-callback/_config.js b/packages/svelte/tests/runtime-runes/samples/state-link-callback/_config.js new file mode 100644 index 0000000000..978fdb14d6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/state-link-callback/_config.js @@ -0,0 +1,22 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: ``, + + test({ assert, target, logs }) { + const [btn1, btn2] = target.querySelectorAll('button'); + + flushSync(() => btn1.click()); + assert.deepEqual(logs, ['in callback 1']); + assert.htmlEqual(target.innerHTML, ``); + + flushSync(() => btn2.click()); + assert.deepEqual(logs, ['in callback 1']); + assert.htmlEqual(target.innerHTML, ``); + + flushSync(() => btn1.click()); + assert.deepEqual(logs, ['in callback 1', 'in callback 2']); + assert.htmlEqual(target.innerHTML, ``); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/state-link-callback/main.svelte b/packages/svelte/tests/runtime-runes/samples/state-link-callback/main.svelte new file mode 100644 index 0000000000..8ef97c454f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/state-link-callback/main.svelte @@ -0,0 +1,10 @@ + + + +