-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Description
Describe the bug
Suppose I have a form which has initial values dependent on some sort of reactive data. It can be $state, $derived, etc. I need to have that data in the initial value of some input fields. I want all the fields to be loaded during SSR so that users don't see unloaded fields before hydration, so $effect seems to be out of the question.
This is what I initially tried:
<script lang="ts">
import { page } from "$app/state";
import { resolve } from "$app/paths";
import { loadId, sendId } from "../../data.remote";
const idPrefix = $derived(page.params.idPrefix ?? "default");
const id = $derived(await loadId({ idPrefix }));
const form = $derived(sendId.for(id.received));
form.fields.set({ text: `Default text for ${id.received}` });
</script>
<form {...form}>
<input
type="text"
placeholder="Enter a value to send to the server"
{...form.fields.text.as("text")}
/>
<button type="submit">Submit</button>
</form>This doesn't work, as I'm referencing form and id outside of a closure. If I were to do a client navigation to the same page but different params, I'd get stale input fields.
I then tried to put the form.fields.set call in form's $derived:
const form = $derived.by(() => {
const form = sendId.for(id.received);
form.fields.set({ text: `Default text for ${id.received}` });
return form;
});This doesn't work either, as form.fields.set is itself a state mutation, triggering this error when e.g. submitting the form:
Uncaught Svelte error: state_unsafe_mutation
Updating state inside `$derived(...)`, `$inspect(...)` or a template expression is forbidden. If the value should not be reactive, declare it without `$state`
https://svelte.dev/e/state_unsafe_mutation
Currently I'm using a workaround like so:
const form = $derived.by(() => {
const text = `Default text for ${id.received}`;
const form = sendId.for(id.received);
untrack(() => form.fields.set({ text }));
return form;
});This works, but all the initial values have to be calculated outside of untrack, which isn't quite neat.
I supposed that the form remote function might be lacking some sort of initial value mechanism, which is why I filed an issue. What I have in mind would be a bit similar to .for, such as .values({ text: `Default text for ${id.received}` }).
Another solution I have in mind would be to make .as accept an additional defaultValue argument like so: form.fields.text.as("text", `Default text for ${id.received}`). This feels more ergonomic in my opinion; however, since the fields seem to be controlled by states, I don't think it's as viable as the aforementioned.
Thank you!
Reproduction
https://github.com/DuCanhGH/sv-reprod-remote-reactive
Logs
This reference only captures the initial value of `form`. Did you mean to reference it inside a closure instead?
https://svelte.dev/e/state_referenced_locally svelte (state_referenced_locally)
Uncaught Svelte error: state_unsafe_mutation
Updating state inside `$derived(...)`, `$inspect(...)` or a template expression is forbidden. If the value should not be reactive, declare it without `$state`
https://svelte.dev/e/state_unsafe_mutationSystem Info
System:
OS: Linux 6.18 cpe:/o:nixos:nixos:26.05 26.05 (Yarara)
CPU: (16) x64 AMD Ryzen 7 9800X3D 8-Core Processor
Memory: 14.85 GB / 30.51 GB
Container: Yes
Shell: 4.5.0 - /run/current-system/sw/bin/fish
Binaries:
Node: 22.22.0 - /run/current-system/sw/bin/node
Yarn: 1.22.22 - /run/current-system/sw/bin/yarn
npm: 10.9.4 - /run/current-system/sw/bin/npm
pnpm: 10.29.1 - /run/current-system/sw/bin/pnpm
Browsers:
Chromium: 145.0.7632.116
Firefox: 148.0
Firefox Developer Edition: 148.0
npmPackages:
@sveltejs/adapter-auto: ^7.0.0 => 7.0.1
@sveltejs/kit: ^2.50.2 => 2.53.4
@sveltejs/vite-plugin-svelte: ^6.2.4 => 6.2.4
svelte: ^5.51.0 => 5.53.7
vite: ^7.3.1 => 7.3.1Severity
annoyance
Additional Information
No response