fix: invoke `$state.link` callback at the correct time

state-link-fix
Rich Harris 3 months ago
parent 60a71cc593
commit 1534c1d0bb

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: invoke `$state.link` callback at the correct time

@ -27,6 +27,7 @@ import {
} from '../constants.js'; } from '../constants.js';
import * as e from '../errors.js'; import * as e from '../errors.js';
import { derived } from './deriveds.js'; import { derived } from './deriveds.js';
import { render_effect } from './effects.js';
let inspect_effects = new Set(); let inspect_effects = new Set();
@ -53,44 +54,23 @@ export function source(v) {
* @returns {(value?: V) => V} * @returns {(value?: V) => V}
*/ */
export function source_link(get_value, callback) { export function source_link(get_value, callback) {
var was_local = false; var s = source(/** @type {V} */ (undefined));
var init = false; var ran = false;
var local_source = source(/** @type {V} */ (undefined));
var linked_derived = derived(() => { callback ??= (value) => set(s, value);
var local_value = /** @type {V} */ (get(local_source));
var linked_value = get_value();
if (was_local) { render_effect(() => {
was_local = false; if (ran) {
return local_value; callback(get_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;
}
} else { } 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);
}; };
} }

@ -0,0 +1,22 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
html: `<button>0</button><button>0</button>`,
test({ assert, target, logs }) {
const [btn1, btn2] = target.querySelectorAll('button');
flushSync(() => btn1.click());
assert.deepEqual(logs, ['in callback 1']);
assert.htmlEqual(target.innerHTML, `<button>1</button><button>1</button>`);
flushSync(() => btn2.click());
assert.deepEqual(logs, ['in callback 1']);
assert.htmlEqual(target.innerHTML, `<button>1</button><button>2</button>`);
flushSync(() => btn1.click());
assert.deepEqual(logs, ['in callback 1', 'in callback 2']);
assert.htmlEqual(target.innerHTML, `<button>2</button><button>2</button>`);
}
});

@ -0,0 +1,10 @@
<script>
let a = $state(0);
let b = $state.link(a, (value) => {
console.log(`in callback ${value}`);
b = value;
});
</script>
<button onclick={() => a++}>{a}</button>
<button onclick={() => b++}>{b}</button>
Loading…
Cancel
Save