Skip to content

Commit 1534c1d

Browse files
committed
fix: invoke $state.link callback at the correct time
1 parent 60a71cc commit 1534c1d

File tree

4 files changed

+49
-32
lines changed

4 files changed

+49
-32
lines changed

.changeset/proud-dots-swim.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: invoke `$state.link` callback at the correct time

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

+12-32
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
} from '../constants.js';
2828
import * as e from '../errors.js';
2929
import { derived } from './deriveds.js';
30+
import { render_effect } from './effects.js';
3031

3132
let inspect_effects = new Set();
3233

@@ -53,44 +54,23 @@ export function source(v) {
5354
* @returns {(value?: V) => V}
5455
*/
5556
export function source_link(get_value, callback) {
56-
var was_local = false;
57-
var init = false;
58-
var local_source = source(/** @type {V} */ (undefined));
57+
var s = source(/** @type {V} */ (undefined));
58+
var ran = false;
5959

60-
var linked_derived = derived(() => {
61-
var local_value = /** @type {V} */ (get(local_source));
62-
var linked_value = get_value();
60+
callback ??= (value) => set(s, value);
6361

64-
if (was_local) {
65-
was_local = false;
66-
return local_value;
67-
}
68-
69-
return linked_value;
70-
});
71-
72-
return function (/** @type {any} */ value) {
73-
if (arguments.length > 0) {
74-
was_local = true;
75-
set(local_source, value);
76-
get(linked_derived);
77-
return value;
78-
}
79-
80-
var linked_value = get(linked_derived);
81-
82-
if (init) {
83-
if (callback !== undefined) {
84-
untrack(() => callback(linked_value));
85-
return local_source.v;
86-
}
62+
render_effect(() => {
63+
if (ran) {
64+
callback(get_value());
8765
} else {
88-
init = true;
66+
s.v = get_value();
8967
}
68+
});
9069

91-
local_source.v = linked_value;
70+
ran = true;
9271

93-
return linked_value;
72+
return function (/** @type {any} */ value) {
73+
return arguments.length === 1 ? set(s, /** @type {V} */ (value)) : get(s);
9474
};
9575
}
9676

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
html: `<button>0</button><button>0</button>`,
6+
7+
test({ assert, target, logs }) {
8+
const [btn1, btn2] = target.querySelectorAll('button');
9+
10+
flushSync(() => btn1.click());
11+
assert.deepEqual(logs, ['in callback 1']);
12+
assert.htmlEqual(target.innerHTML, `<button>1</button><button>1</button>`);
13+
14+
flushSync(() => btn2.click());
15+
assert.deepEqual(logs, ['in callback 1']);
16+
assert.htmlEqual(target.innerHTML, `<button>1</button><button>2</button>`);
17+
18+
flushSync(() => btn1.click());
19+
assert.deepEqual(logs, ['in callback 1', 'in callback 2']);
20+
assert.htmlEqual(target.innerHTML, `<button>2</button><button>2</button>`);
21+
}
22+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<script>
2+
let a = $state(0);
3+
let b = $state.link(a, (value) => {
4+
console.log(`in callback ${value}`);
5+
b = value;
6+
});
7+
</script>
8+
9+
<button onclick={() => a++}>{a}</button>
10+
<button onclick={() => b++}>{b}</button>

0 commit comments

Comments
 (0)