-
Hi I have a Svelte5 project where I need to dynamically create input forms of various kinds. And dynamically assign values.
Here is a toy project I made that hopefully illustrates the issue. I cannot figure out how to do get rid of this warning, without getting other ones, for example with mutating $state inside $effect. // npx sv create . SvelteKit minimal / Typescript / add nothing / npm
// src/routes/+page.svelte
<script lang="ts">
interface Field<T> {
name: string
type: string,
defaultValue: T,
validCheck: (value: T) => boolean
}
const toEmail : Field<string> = {
name: "Email",
type: "text",
defaultValue: "reply@example.com",
validCheck: value => {
return value.includes("@");
}
};
const toNumber : Field<number>= {
name: "PhoneNumber",
type: "number",
defaultValue: 0,
validCheck: value => {
return value > 0;
}
};
const subject : Field<string>= {
name: "Subject",
type: "text",
defaultValue: "Re:",
validCheck: value => {
return value.length > 2;
}
};
const message : Field<string>= {
name: "Message",
type: "textarea",
defaultValue: "",
validCheck: value => {
return value.includes("Hello");
}
};
const tasks = ["email", "sms"];
const taskParameters: {[id:string]: Field<any>[]} = {
email: [toEmail, subject, message],
sms: [toNumber, message]
};
let selectedTask = $state("sms");
let parameters = $derived(taskParameters[selectedTask]);
let values = $derived(taskParameters[selectedTask].map(item => item.defaultValue));
let valid : boolean[] = $derived.by(() => {
console.log("valid", selectedTask);
return values.map((item, idx) => {
return taskParameters[selectedTask][idx].validCheck(item)
});
});
let send = () => {
if (selectedTask == "email") {
console.log("Sending email", $state.snapshot(values));
} else if (selectedTask == "sms") {
console.log("Sending SMS", $state.snapshot(values));
}
}
</script>
{#each tasks as task, i}
<input type="radio" class="radio-accent radio" value={task} id="input_radio_{task}" bind:group={selectedTask}/>
<label for="input_radio_{task}">{task}</label><br>
{/each}
{#each parameters as param, i}
<label for="{param.name}">{param.name}</label>
{#if param.type == "text"}
<input bind:value={values[i]} id="{param.name}" class={[!valid[i] && "invalid"]}/>
{/if}
{#if param.type == "textarea"}
<textarea bind:value={values[i]} id="{param.name}" class={[!valid[i] && "invalid"]}></textarea>
{/if}
{#if param.type == "number"}
<input bind:value={values[i]} class={[!valid[i] && "invalid"]}/>
{/if}
<br>
{/each}
<button onclick={send}>
Send
</button> |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
Since the let values = $derived.by(() => {
const state = $state(
taskParameters[selectedTask].map(item => item.defaultValue)
);
return state;
}); Currently a separate declaration is required to create a (By the way, you can add syntax highlighting to code by specifying a language after the first backtick fence, e.g. in your component code |
Beta Was this translation helpful? Give feedback.
Since the
$derived
returns a plain object, you end up binding to a non-reactive object.You can create a reactive object within the derived like this:
Currently a separate declaration is required to create a
$state
object, i.e. it cannot be returned directly.(By the way, you can add syntax highlighting to code by specifying a language after the first backtick fence, e.g. in your component code
```svelte
)