pull/18686/merge
Marwan 5 days ago committed by GitHub
commit a788723367
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: warn when reassigning derived state on server — overrides are permanent unlike client where they reset when dependencies change. Document server/client divergence in $derived docs (sveltejs/svelte#18681)

@ -24,6 +24,8 @@ As with `$state`, you can mark class fields as `$derived`.
> [!NOTE] Code in Svelte components is only executed once at creation. Without the `$derived` rune, `doubled` would maintain its original value even when `count` changes. > [!NOTE] Code in Svelte components is only executed once at creation. Without the `$derived` rune, `doubled` would maintain its original value even when `count` changes.
> [!NOTE] There is no reactivity on the server — `$derived` and `$derived.by` values do not recompute when their dependencies change, including overridden values (see [Overriding derived values](#Overriding-derived-values)).
## `$derived.by` ## `$derived.by`
Sometimes you need to create complex derivations that don't fit inside a short expression. In these cases, you can use `$derived.by` which accepts a function as its argument. Sometimes you need to create complex derivations that don't fit inside a short expression. In these cases, you can use `$derived.by` which accepts a function as its argument.
@ -93,6 +95,8 @@ Derived expressions are recalculated when their dependencies change, but you can
> [!NOTE] Prior to Svelte 5.25, deriveds were read-only. > [!NOTE] Prior to Svelte 5.25, deriveds were read-only.
> [!WARNING] There is no reactivity on the server — when a `$derived` or `$derived.by` value's dependencies change, it will not recompute. This includes overridden values, which on the client are temporary and discarded when dependencies change, but on the server are permanent.
## Deriveds and reactivity ## Deriveds and reactivity
Unlike `$state`, which converts objects and arrays to [deeply reactive proxies]($state#Deep-state), `$derived` values are left as-is. For example, [in a case like this](/playground/untitled#H4sIAAAAAAAAE4VU22rjMBD9lUHd3aaQi9PdstS1A3t5XvpQ2Ic4D7I1iUUV2UjjNMX431eS7TRdSosxgjMzZ45mjt0yzffIYibvy0ojFJWqDKCQVBk2ZVup0LJ43TJ6rn2aBxw-FP2o67k9oCKP5dziW3hRaUJNjoYltjCyplWmM1JIIAn3FlL4ZIkTTtYez6jtj4w8WwyXv9GiIXiQxLVs9pfTMR7EuoSLIuLFbX7Z4930bZo_nBrD1bs834tlfvsBz9_SyX6PZXu9XaL4gOWn4sXjeyzftv4ZWfyxubpzxzg6LfD4MrooxELEosKCUPigQCMPKCZh0OtQE1iSxcsmdHuBvCiHZXALLXiN08EL3RRkaJ_kDVGle0HcSD5TPEeVtj67O4Nrg9aiSNtBY5oODJkrL5QsHtN2cgXp6nSJMWzpWWGasdlsGEMbzi5jPr5KFr0Ep7pdeM2-TCelCddIhDxAobi1jqF3cMaC1RKp64bAW9iFAmXGIHfd4wNXDabtOLN53w8W53VvJoZLh7xk4Rr3CoL-UNoLhWHrT1JQGcM17u96oES5K-kc2XOzkzqGCKL5De79OUTyyrg1zgwXsrEx3ESfx4Bz0M5UjVMHB24mw9SuXtXFoN13fYKOM1tyUT3FbvbWmSWCZX2Er-41u5xPoml45svRahl9Wb9aasbINJixDZwcPTbyTLZSUsAvrg_cPuCR7s782_WU8343Y72Qtlb8OYatwuOQvuN13M_hJKNfxann1v1U_B1KZ_D_mzhzhz24fw85CSz2irtN9w9HshBK7AQAAA==)... Unlike `$state`, which converts objects and arrays to [deeply reactive proxies]($state#Deep-state), `$derived` values are left as-is. For example, [in a case like this](/playground/untitled#H4sIAAAAAAAAE4VU22rjMBD9lUHd3aaQi9PdstS1A3t5XvpQ2Ic4D7I1iUUV2UjjNMX431eS7TRdSosxgjMzZ45mjt0yzffIYibvy0ojFJWqDKCQVBk2ZVup0LJ43TJ6rn2aBxw-FP2o67k9oCKP5dziW3hRaUJNjoYltjCyplWmM1JIIAn3FlL4ZIkTTtYez6jtj4w8WwyXv9GiIXiQxLVs9pfTMR7EuoSLIuLFbX7Z4930bZo_nBrD1bs834tlfvsBz9_SyX6PZXu9XaL4gOWn4sXjeyzftv4ZWfyxubpzxzg6LfD4MrooxELEosKCUPigQCMPKCZh0OtQE1iSxcsmdHuBvCiHZXALLXiN08EL3RRkaJ_kDVGle0HcSD5TPEeVtj67O4Nrg9aiSNtBY5oODJkrL5QsHtN2cgXp6nSJMWzpWWGasdlsGEMbzi5jPr5KFr0Ep7pdeM2-TCelCddIhDxAobi1jqF3cMaC1RKp64bAW9iFAmXGIHfd4wNXDabtOLN53w8W53VvJoZLh7xk4Rr3CoL-UNoLhWHrT1JQGcM17u96oES5K-kc2XOzkzqGCKL5De79OUTyyrg1zgwXsrEx3ESfx4Bz0M5UjVMHB24mw9SuXtXFoN13fYKOM1tyUT3FbvbWmSWCZX2Er-41u5xPoml45svRahl9Wb9aasbINJixDZwcPTbyTLZSUsAvrg_cPuCR7s782_WU8343Y72Qtlb8OYatwuOQvuN13M_hJKNfxann1v1U_B1KZ_D_mzhzhz24fw85CSz2irtN9w9HshBK7AQAAA==)...

@ -1,5 +1,13 @@
<!-- This file is generated by scripts/process-messages/index.js. Do not edit! --> <!-- This file is generated by scripts/process-messages/index.js. Do not edit! -->
### derived_reassignment
```
Assignment to derived state on the server is permanent and will not be recalculated when its dependencies change, unlike on the client where it is temporary
```
This warning is emitted when you reassign a value created with `$derived` or `$derived.by` while compiling with `generate: 'server'`. There is no reactivity on the server — derived values do not recompute when dependencies change, including overridden values. On the client, overrides are temporary and discarded when dependencies change.
### unresolved_hydratable ### unresolved_hydratable
``` ```

@ -1,3 +1,9 @@
## derived_reassignment
> Assignment to derived state on the server is permanent and will not be recalculated when its dependencies change, unlike on the client where it is temporary
This warning is emitted when you reassign a value created with `$derived` or `$derived.by` while compiling with `generate: 'server'`. There is no reactivity on the server — derived values do not recompute when dependencies change, including overridden values. On the client, overrides are temporary and discarded when dependencies change.
## unresolved_hydratable ## unresolved_hydratable
> A `hydratable` value with key `%key%` was created, but at least part of it was not used during the render. > A `hydratable` value with key `%key%` was created, but at least part of it was not used during the render.

@ -102,9 +102,9 @@ function build_assignment(operator, left, right, context) {
const binding = context.state.scope.get(object.name); const binding = context.state.scope.get(object.name);
// TODO 6.0 this won't work perfectly: once a derived is written to, it will // Derived reassignment on the server is permanent (no reactive graph).
// no longer recompute. It might be better to disallow writing to deriveds // We now warn in DEV via `derived_reassignment` in `src/internal/server/index.js`
// on the server, to prevent this bug occurring // See sveltejs/svelte#18681. TODO 6.0: consider disallowing writes to deriveds on server.
if (binding?.kind === 'derived' && object === left) { if (binding?.kind === 'derived' && object === left) {
let value = /** @type {Expression} */ ( let value = /** @type {Expression} */ (
context.visit(build_assignment_value(operator, left, right)) context.visit(build_assignment_value(operator, left, right))

@ -23,6 +23,7 @@ import {
} from '../../utils.js'; } from '../../utils.js';
import { Renderer } from './renderer.js'; import { Renderer } from './renderer.js';
import * as e from './errors.js'; import * as e from './errors.js';
import * as w from './warnings.js';
import { ssr_context } from './context.js'; import { ssr_context } from './context.js';
// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
@ -505,6 +506,8 @@ export function derived(fn) {
return updated_value ?? get_value(); return updated_value ?? get_value();
} }
if (DEV) w.derived_reassignment();
updated_value = new_value; updated_value = new_value;
return updated_value; return updated_value;
}; };

@ -5,6 +5,17 @@ import { DEV } from 'esm-env';
var bold = 'font-weight: bold'; var bold = 'font-weight: bold';
var normal = 'font-weight: normal'; var normal = 'font-weight: normal';
/**
* Assignment to derived state on the server is permanent and will not be recalculated when its dependencies change, unlike on the client where it is temporary
*/
export function derived_reassignment() {
if (DEV) {
console.warn(`%c[svelte] derived_reassignment\n%cAssignment to derived state on the server is permanent and will not be recalculated when its dependencies change, unlike on the client where it is temporary\nhttps://svelte.dev/e/derived_reassignment`, bold, normal);
} else {
console.warn(`https://svelte.dev/e/derived_reassignment`);
}
}
/** /**
* A `hydratable` value with key `%key%` was created, but at least part of it was not used during the render. * A `hydratable` value with key `%key%` was created, but at least part of it was not used during the render.
* *

Loading…
Cancel
Save