T
TanStack2mo ago
fascinating-indigo

Form Composition Question

I'm learning how to do form composition and I wanted to check if this is expected behavior:
<form.AppField
name="formName"
children={(field) => (
<>
<FieldLabel>Form name*</FieldLabel>
<FieldDescription>Enter the name of the form</FieldDescription>
<Input
aria-describedby={`${field.name}-description ${field.name}-errors`}
/>
<FieldErrors />
</>
)}
/>
<form.AppField
name="formName"
children={(field) => (
<>
<FieldLabel>Form name*</FieldLabel>
<FieldDescription>Enter the name of the form</FieldDescription>
<Input
aria-describedby={`${field.name}-description ${field.name}-errors`}
/>
<FieldErrors />
</>
)}
/>
I'm using useFieldContext() in each of the components and despite not registering them in my createFormHook() or using field.[ComponentName] under my AppField rendering function, they work as expected. Is this fully erroneous or more of a "it works but don't do that" situation? Also tangential question while I'm here: Is there a more concise/efficient way to register each of my components without manually using value={...} and onChange={...} for each one?
3 Replies
harsh-harlequin
harsh-harlequin2mo ago
It works because it's passed through React Context. You won't get autocomplete suggestions though, so those can be very convenient
Also tangential question while I'm here: Is there a more concise/efficient way to register each of my components without manually using value={...} and onChange={...} for each one?
I'm not sure what your request is here. Form Composition allows you to reduce the boilerplate by filling it out once and then passing the field component
fascinating-indigo
fascinating-indigoOP2mo ago
You're right, now that I'm thinking of it, it was kind of a silly question I was just somewhat fixated on shadcn's react hook form wrappers as they seemed convenient but that's just more boilerplate when I think of it Also big thanks for the fast responses !
harsh-harlequin
harsh-harlequin2mo ago
Here's the thing: If it's too granular for you, you can make a second field component around the shadcn components
// Goal: Make field with label, description, input and feedback all in one

export function InputField({ label, description }) {
const field = useFieldContext();
return <>
<>
<FieldLabel>{label}</FieldLabel>
<FieldDescription>{description}</FieldDescription>
<Input
aria-describedby={`${field.name}-description ${field.name}-errors`}
/>
<FieldErrors />
</>
</>
}

<form.AppField
name="formName"
children={(field) => (
<field.InputField label="Form name*" description="Enter the name of the form"/>
)}
/>
// Goal: Make field with label, description, input and feedback all in one

export function InputField({ label, description }) {
const field = useFieldContext();
return <>
<>
<FieldLabel>{label}</FieldLabel>
<FieldDescription>{description}</FieldDescription>
<Input
aria-describedby={`${field.name}-description ${field.name}-errors`}
/>
<FieldErrors />
</>
</>
}

<form.AppField
name="formName"
children={(field) => (
<field.InputField label="Form name*" description="Enter the name of the form"/>
)}
/>
since it's context, it can have other nested contexts within

Did you find this page helpful?