Guidance for Forms for Nested Types
Unanswered
VoidPointer posted this in #help-forum
Can someone please suggest a good resource for implementing forms for nested types, like for my
I have, after many lessons, got some forms working using
Basics:export type Basics = {
name?: string;
location?: Location;
profiles?: Profile[];
};I have, after many lessons, got some forms working using
useActionState and zod-form-data for flat types and I'd like to extend that to nested ones. Gemini has been giving me a bit of a run around for a while, and other guides I've found seem to skirt the issue a bit. My first thought was, why nest vs. just using one form for each type, but having to handle updates to e.g. Location on it's own, then update Basics seems just wrong.9 Replies
does your form have autosaves or does it need a save button?
Nesting would require you to manage states coz you basically are not using the native FormData features.
"use client"
export function BasicsForm(props) {
const [loc, setLoc] = useState(props.location)
const [prof, setProf] = useState(props.profiles)
const [state, action, pending] = useActionState(
async (prev, form) => {
const name = form.get("name")
const inputs: Basics = {
name, locations: loc, profiles: prof
}
const parsed = parseBasicsForm(inputs) // use zod
const res = await serverActionMutateBasics(parsed)
return res
}, null
)
return (
<form action={action}>
<input name="name" defaultValues={props.name} />
<LocationInput values={loc} onValueChange={setLoc} />
<ProfilesInput values={prof} onValueChange={setProf} />
<button type="submit">Submit</button>
</form>
)
}something like this
Haha, sorry, I meant to delete that. I'll try your approach, thanks, merged with what I already have, and see what I can get.
I'm kind of stubbon, and having got somewhere with the nesting last night, I pushed a bit more and got something right. I'm doing this:
and this:
and the log output here has all the wanted data, as well as the log out output on the route that hosts the form component.
...
<HtmlTextField name={"dateOfBirth"} required={true} formState={state} />
<HtmlTextField name={"nationality"} required={true} formState={state} />
<HtmlTextField name={"workAuth"} required={true} label={"Work Authorization"} formState={state} />
<HtmlTextField name={"location.address"} required={true} label={"Address"} formState={state} />
<HtmlTextField name={"location.city"} required={true} label={"City"} formState={state} />
<HtmlTextField name={"location.postalCode"} required={true} label={"Postal Code"} formState={state} />
...and this:
export async function updateBasics(
prevState: EditBasicsFormState,
formData: FormData,
): Promise<EditBasicsFormState> {
const preprocessor = zfd.formData(basicsSchema);
const result = preprocessor.safeParse(formData);
console.log(util.inspect(result, { depth: null }));
const values = Object.fromEntries(
formData,
) as unknown as EditBasicsFormState["values"];and the log output here has all the wanted data, as well as the log out output on the route that hosts the form component.
It's not that I think using separate components is out, I'll be moving the array to a reusable array component, and the Location to a separate dedicated component, just to group the inputs, but this little exercise gave me a good handle on formstate etc.