breaking: remove `$state.link` callback (#12942)

* breaking: remove `$state.link` callback

* simplify

* regenerate
pull/12954/head
Rich Harris 4 months ago committed by GitHub
parent af35fb7ae6
commit 0b51ff04b0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
breaking: remove callback from `$state.link`

@ -89,24 +89,6 @@ console.log(a, b); // 3, 3
As with `$state`, if `$state.link` is passed a plain object or array it will be made deeply reactive. If passed an existing state proxy it will be reused, meaning that mutating the linked state will mutate the original. To clone a state proxy, you can use [`$state.snapshot`](#$state-snapshot).
If you pass a callback to `$state.link`, changes to the input value will invoke the callback rather than updating the linked state, allowing you to choose whether to (for example) preserve or discard local changes, or merge incoming changes with local ones:
```js
let { stuff } = $props();
let incoming = $state();
let hasUnsavedChanges = $state(false);
let current = $state.link({ ...stuff }, (stuff) => {
if (hasUnsavedChanges) {
incoming = stuff;
} else {
incoming = null;
current = stuff;
}
});
```
## `$state.raw`
State declared with `$state.raw` cannot be mutated; it can only be _reassigned_. In other words, rather than assigning to a property of an object, or using an array method like `push`, replace the object or array altogether if you'd like to update it:

@ -148,7 +148,7 @@ declare namespace $state {
*
* @param value The linked value
*/
export function link<T>(value: T, callback?: (value: T) => void): T;
export function link<T>(value: T): T;
export function raw<T>(initial: T): T;
export function raw<T>(): T | undefined;

@ -131,11 +131,7 @@ export function VariableDeclaration(node, context) {
}
if (rune === '$state.link') {
value = b.call(
'$.source_link',
b.thunk(value),
args.length === 2 && /** @type {Expression} */ (context.visit(args[1]))
);
value = b.call('$.source_link', b.thunk(value));
} else if (is_state_source(binding, context.state.analysis)) {
value = b.call('$.source', value);
}

@ -49,12 +49,10 @@ export function source(v) {
/**
* @template V
* @param {() => V} get_value
* @param {(value: V) => void} [callback]
* @returns {(value?: V) => V}
*/
export function source_link(get_value, callback) {
export function source_link(get_value) {
var was_local = false;
var init = false;
var local_source = source(/** @type {V} */ (undefined));
var linked_derived = derived(() => {
@ -77,20 +75,7 @@ export function source_link(get_value, callback) {
return value;
}
var linked_value = get(linked_derived);
if (init) {
if (callback !== undefined) {
untrack(() => callback(linked_value));
return local_source.v;
}
} else {
init = true;
}
local_source.v = linked_value;
return linked_value;
return (local_source.v = get(linked_derived));
};
}

@ -1,38 +0,0 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
html: `<button>0</button><button>0</button><button>false</button>`,
test({ assert, target }) {
const [btn1, btn2, btn3] = target.querySelectorAll('button');
flushSync(() => btn1.click());
assert.htmlEqual(
target.innerHTML,
`<button>1</button><button>1</button><button>false</button>`
);
flushSync(() => btn2.click());
assert.htmlEqual(
target.innerHTML,
`<button>1</button><button>2</button><button>false</button>`
);
flushSync(() => btn3.click());
assert.htmlEqual(target.innerHTML, `<button>1</button><button>2</button><button>true</button>`);
flushSync(() => btn1.click());
assert.htmlEqual(target.innerHTML, `<button>2</button><button>2</button><button>true</button>`);
flushSync(() => btn1.click());
assert.htmlEqual(target.innerHTML, `<button>3</button><button>2</button><button>true</button>`);
flushSync(() => btn1.click());
flushSync(() => btn3.click());
assert.htmlEqual(
target.innerHTML,
`<button>4</button><button>4</button><button>false</button>`
);
}
});

@ -1,12 +0,0 @@
<script>
let a = $state(0);
let b = $state.link(a, (value) => {
if (c) return;
b = value;
});
let c = $state(false);
</script>
<button onclick={() => a++}>{a}</button>
<button onclick={() => b++}>{b}</button>
<button onclick={() => c = !c}>{c}</button>

@ -2937,7 +2937,7 @@ declare namespace $state {
*
* @param value The linked value
*/
export function link<T>(value: T, callback?: (value: T) => void): T;
export function link<T>(value: T): T;
export function raw<T>(initial: T): T;
export function raw<T>(): T | undefined;

@ -91,24 +91,6 @@ console.log(a, b); // 3, 3
As with `$state`, if `$state.link` is passed a plain object or array it will be made deeply reactive. If passed an existing state proxy it will be reused, meaning that mutating the linked state will mutate the original. To clone a state proxy, you can use [`$state.snapshot`](#$state-snapshot).
If you pass a callback to `$state.link`, changes to the input value will invoke the callback rather than updating the linked state, allowing you to choose whether to (for example) preserve or discard local changes, or merge incoming changes with local ones:
```js
let { stuff } = $props();
let incoming = $state();
let hasUnsavedChanges = $state(false);
let current = $state.link({ ...stuff }, (stuff) => {
if (hasUnsavedChanges) {
incoming = stuff;
} else {
incoming = null;
current = stuff;
}
});
```
## `$state.raw`
State declared with `$state.raw` cannot be mutated; it can only be _reassigned_. In other words, rather than assigning to a property of an object, or using an array method like `push`, replace the object or array altogether if you'd like to update it:

Loading…
Cancel
Save