Conditionally rendered fields don't properly update without a key #1095
-
This is pretty obvious in retrospect, but it took me quite some time to realize that this was indeed the problem when debugging the issue in my project 😄 Would love to hear people's ideas for how to improve on the situation! Adding something to the docs would probably be a good first step? The following form consists of two fields:
In the inital/default configuration, the forms works as expected. However, when switching the first field to the other option, something strange happens. The form seemingly updates, rendering a different field in the second spot. However, the value in the text field persists after the switch. And when typing, the state being updated is that of the initially rendered text field, not the field that you would expect to be currently rendered This can be solved by adding a key to each of the conditionally rendered fields. This is easy to screw up if you have many conditionally rendered fields -- missing a single key may create some pretty subtle bugs. const schema = z.discriminatedUnion("tag", [
z.object({ tag: z.literal("short"), string: z.string().nonempty() }),
z.object({ tag: z.literal("long"), text: z.string().nonempty() }),
])
type Schema = z.infer<typeof schema>
const TaggedUnion = () => {
const form = useForm<Schema>({
defaultValues: { tag: "short", string: ""},
validators: {
onChange: schema,
}
});
return <form
onSubmit={e => {
e.preventDefault();
console.log({
tag: form.getFieldValue("tag"),
string: form.getFieldValue("string"),
text: form.getFieldValue("text"),
});
}}
>
<form.Field name="tag">
{
(field) => <select value={field.state.value} onChange={e => field.handleChange(e.target.value)}>
<option value="short">Short</option>
<option value="long">Long</option>
</select>
}
</form.Field>
<form.Subscribe
selector={(state) => [state.values.tag]}
children={
([tag]: ["short" | "long"]) => {
if (tag === "short") {
return <form.Field name="string">
{
(field) => <input value={field.state.value ?? ""} onChange={e => field.handleChange(e.target.value)}/>
}
</form.Field>
} else if (tag === "long") {
return <form.Field name="text">
{
(field) => <textarea value={field.state.value ?? ""} onChange={e => field.handleChange(e.target.value)}/>
}
</form.Field>
}
}
}
/>
<button>Submit</button>
</form>;
}; |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
Hmmm, I think this is a bug:
I'll look at it tomorrow. Can't promise a quick fix though, it looks complicated... 😅 Thanks for bringing it up! |
Beta Was this translation helpful? Give feedback.
Hmmm, I think this is a bug:
I'll look at it tomorrow. Can't promise a quick fix though, it looks complicated... 😅
Thanks for bringing it up!