Skip to content

docs: tweak $state.is warning #12599

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion packages/svelte/messages/client-warnings/warnings.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,24 @@

## state_proxy_equality_mismatch

> Reactive `$state(...)` proxies and the values they proxy have different identities. Because of this, comparisons with `%operator%` will produce unexpected results. Consider using `$state.is(a, b)` instead
> Reactive `$state(...)` proxies and the values they proxy have different identities. Because of this, comparisons with `%operator%` will produce unexpected results. Consider using `$state.is(a, b)` instead%details%

`$state(...)` does create a proxy of the value it is passed. Therefore, the resulting object has a different identity and equality checks will always return `false`:

```svelte
<script>
let object = { foo: 'bar' };
let state_object = $state(object);
object === state_object; // always false
</script>
```

Most of the time this will not be a problem in practise because it is very rare to keep the original value around to later compare it against a state value. In case it happens, Svelte will warn you about it in development mode and suggest to use `$state.is` instead, which is able to unwrap the proxy and compare the original values:

```svelte
<script>
let object = { foo: 'bar' };
let state_object = $state(object);
$state.is(object, state_object); // true
</script>
```
2 changes: 1 addition & 1 deletion packages/svelte/scripts/process-messages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ for (const category of fs.readdirSync('messages')) {

const sections = text.trim().split('\n\n');
let details = null;
if (!sections[sections.length - 1].startsWith('> ')) {
while (!sections[sections.length - 1].startsWith('> ')) {
details = /** @type {string} */ (sections.pop());
}

Expand Down
19 changes: 14 additions & 5 deletions packages/svelte/src/internal/client/dev/equality.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ export function init_array_prototype_warnings() {
const test = indexOf.call(get_proxied_value(this), get_proxied_value(item), from_index);

if (test !== -1) {
w.state_proxy_equality_mismatch('array.indexOf(...)');
w.state_proxy_equality_mismatch(
'array.indexOf(...)',
': `array.findIndex(entry => $state.is(entry, item))`'
);
}
}

Expand All @@ -42,7 +45,10 @@ export function init_array_prototype_warnings() {
);

if (test !== -1) {
w.state_proxy_equality_mismatch('array.lastIndexOf(...)');
w.state_proxy_equality_mismatch(
'array.lastIndexOf(...)',
': `array.findLastIndex(entry => $state.is(entry, item))`'
);
}
}

Expand All @@ -56,7 +62,10 @@ export function init_array_prototype_warnings() {
const test = includes.call(get_proxied_value(this), get_proxied_value(item), from_index);

if (test) {
w.state_proxy_equality_mismatch('array.includes(...)');
w.state_proxy_equality_mismatch(
'array.includes(...)',
': `array.some(entry => $state.is(entry, item))`'
);
}
}

Expand All @@ -79,7 +88,7 @@ export function init_array_prototype_warnings() {
*/
export function strict_equals(a, b, equal = true) {
if ((a === b) !== (get_proxied_value(a) === get_proxied_value(b))) {
w.state_proxy_equality_mismatch(equal ? '===' : '!==');
w.state_proxy_equality_mismatch(equal ? '===' : '!==', '');
}

return (a === b) === equal;
Expand All @@ -93,7 +102,7 @@ export function strict_equals(a, b, equal = true) {
*/
export function equals(a, b, equal = true) {
if ((a == b) !== (get_proxied_value(a) == get_proxied_value(b))) {
w.state_proxy_equality_mismatch(equal ? '==' : '!=');
w.state_proxy_equality_mismatch(equal ? '==' : '!=', '');
}

return (a == b) === equal;
Expand Down
7 changes: 4 additions & 3 deletions packages/svelte/src/internal/client/warnings.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,13 @@ export function ownership_invalid_mutation(component, owner) {
}

/**
* Reactive `$state(...)` proxies and the values they proxy have different identities. Because of this, comparisons with `%operator%` will produce unexpected results. Consider using `$state.is(a, b)` instead
* Reactive `$state(...)` proxies and the values they proxy have different identities. Because of this, comparisons with `%operator%` will produce unexpected results. Consider using `$state.is(a, b)` instead%details%
* @param {string} operator
* @param {string} details
*/
export function state_proxy_equality_mismatch(operator) {
export function state_proxy_equality_mismatch(operator, details) {
if (DEV) {
console.warn(`%c[svelte] state_proxy_equality_mismatch\n%cReactive \`$state(...)\` proxies and the values they proxy have different identities. Because of this, comparisons with \`${operator}\` will produce unexpected results. Consider using \`$state.is(a, b)\` instead`, bold, normal);
console.warn(`%c[svelte] state_proxy_equality_mismatch\n%cReactive \`$state(...)\` proxies and the values they proxy have different identities. Because of this, comparisons with \`${operator}\` will produce unexpected results. Consider using \`$state.is(a, b)\` instead${details}`, bold, normal);
} else {
// TODO print a link to the documentation
console.warn("state_proxy_equality_mismatch");
Expand Down
Loading