diff --git a/packages/svelte/messages/client-warnings/warnings.md b/packages/svelte/messages/client-warnings/warnings.md index 7a5a495251..695776dd60 100644 --- a/packages/svelte/messages/client-warnings/warnings.md +++ b/packages/svelte/messages/client-warnings/warnings.md @@ -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 + +``` + +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 + +``` diff --git a/packages/svelte/scripts/process-messages/index.js b/packages/svelte/scripts/process-messages/index.js index 5eea56fa04..8ee58cad9d 100644 --- a/packages/svelte/scripts/process-messages/index.js +++ b/packages/svelte/scripts/process-messages/index.js @@ -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()); } diff --git a/packages/svelte/src/internal/client/dev/equality.js b/packages/svelte/src/internal/client/dev/equality.js index c1c392ba87..67fc039da1 100644 --- a/packages/svelte/src/internal/client/dev/equality.js +++ b/packages/svelte/src/internal/client/dev/equality.js @@ -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))`' + ); } } @@ -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))`' + ); } } @@ -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))`' + ); } } @@ -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; @@ -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; diff --git a/packages/svelte/src/internal/client/warnings.js b/packages/svelte/src/internal/client/warnings.js index 31f67a725a..23c00f971d 100644 --- a/packages/svelte/src/internal/client/warnings.js +++ b/packages/svelte/src/internal/client/warnings.js @@ -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");