React Hook Form and breaking up form components

I'm trying to convert a previous app to t3 stack and use some of the libs that I've seen theo use to simplify the code and make it work more consistently. So far most things are going great, but the point I'm stuck on is converting some of my more complex forms to react-hook-form. For some background my most complex form currently consists of a few reusable "dumb" form segments. These segments take an object as a value and call an onChange() function with an updated version of the whole object whenever one of the fields is modified. The top-level component has a state variable that contains all of these sub-objects. When the user is done with the form the entire object is checked and submitted to the api endpoint. These are the issues I'm having: 1) My first thought was to treat each one of my dumb form segments as a controlled input wrapped with a <Controller />. However, if I want to be able to display errors on the text fields in the sub-component I don't think I can do that. Is this correct? 2) Assuming I can't do (1), then I need to pass the form methods down into each form segment to allow me to access the errors objects. Here, typing becomes an issue. The form segment is designed to be re-used in several different contexts, not always as part of the same form. So when I pass the form methods down to the segment I don't know what generic type to give them. I know the type that this segment of the form is supposed to deal with, but the controller passed down to me is for the top-level form, not this subsection. Below is a simplified example:
function FormSegment1(props: FormSegment1Props) {
// The formMethods returned here has the type of `MyForm.settings`
// How can I say here that I don't know the type of the whole object
// but that some part of it will be a `{ firstName: string, lastName: string }`
// so that I can reference those fields / types here?
const formMethods = useFormContext();

...
}

function MyForm1() {
const [settings, setSettings] = useState({
//a bunch of other things
formSegment1: {
firstName: "",
lastName: "",
}
})

return (
<FormProvider {...methods}>
<form>
<FormSegment1 />
</form>
</FormProvider>
)
}
function FormSegment1(props: FormSegment1Props) {
// The formMethods returned here has the type of `MyForm.settings`
// How can I say here that I don't know the type of the whole object
// but that some part of it will be a `{ firstName: string, lastName: string }`
// so that I can reference those fields / types here?
const formMethods = useFormContext();

...
}

function MyForm1() {
const [settings, setSettings] = useState({
//a bunch of other things
formSegment1: {
firstName: "",
lastName: "",
}
})

return (
<FormProvider {...methods}>
<form>
<FormSegment1 />
</form>
</FormProvider>
)
}
3) In general, this all feels very complicated. I really want to have reusable form segments because some parts of my forms get quite complex and I need to decompose them. Is react-hook-form the best tool for this job?
18 Replies
cdialpha
cdialpha3y ago
Also a newb here so I might be wrong, Would you be looking for connect and not Controller ? Isn't controller for uncontrolled components? I know it's just illustrative, but a little hard to tell without seeing what's inside <FormSegment1 />
Tom
Tom3y ago
Would you be looking for connect and not Controller ?
Tom
Tom3y ago
from their docs:
Tom
Tom3y ago
so i think im right that controller is for controlled inputs
Tom
Tom3y ago
As for connect vs controller im pretty sure youre referring to this:
Tom
Tom3y ago
this doesnt really solve any problems as far as i can tell. the ConnectForm is just a component you can make yourself that would make it slightly more ergonomic to use the FromContext maybe you can explain a little more?
I know it's just illustrative, but a little hard to tell without seeing what's inside
its tricky. my actual components are pretty complicated some of them are just a wrapper around a few inputs some of them have arrays and dynamic forms inside them didn't want to overwhelm people with a mass of code
cdialpha
cdialpha3y ago
I was under the impression that Controller was for uncontrolled components... no? Based on your description of it being a wrapper component, that sounds like the intended use case for Connect Form, yeah?
Tom
Tom3y ago
connect form would help, but it doesnt let me dereference the type correctly afaict because the returned register function is still based on the root form's type, not the subcomponent im trying to deal with in the subcomponent
cdialpha
cdialpha3y ago
I was having difficulty with this last week. Trying to get a file upload component to work. I did a hacky work around where I passed state up and down, used an invisible input, and then used setValue (from RHF. I'm sure it'd make a senior dev's eyes bleed. but it seemed to get the job done. probs not helpful for you tho haha. sorry can you clarify? you mean like input, select, textarea, radio, etc ?
Tom
Tom3y ago
Not quite. It's a bit complicated but I'll try to explain I have a big form that's represented by several js objects Each object represents a sub form So like 1 section will ask for your name, address, email etc Another will ask for stuff more specific to the website But the useForm() is based on the big top level form object If I pass that down to a child component that deals with the name address etc Then it doesn't know how to access the fields it needs and it doesn't know the type of the useForm () methods passed to it I hope that makes sense So I lose all my type safety and everything in the cold components Child*
cdialpha
cdialpha3y ago
And it would be too disruptive to the project to totally refactor? Yeah sorry, I better let someone with more knowledge jump in. Thanks for the extra context tho 🙏🏼
Tom
Tom3y ago
This is the refactor :p The problem is that the components are pretty complicated on their own There's a bunch that ate managing arrays with multiple fields
cdialpha
cdialpha3y ago
hahaha my b. Yeah seems like there's gotta be a simpler solution than working with large complicated objects.
Tom
Tom3y ago
Are*
cdialpha
cdialpha3y ago
but easier said than done, obviously.
Tom
Tom3y ago
Yeah. I wish so but i kinda don't believe it. The biggest problem is that there's a 7 step wizard for setting up things I want to simplify it but I want to do that after I get everything working again and I can't really remove things. I can just make them look simpler
Unknown User
Unknown User3y ago
Message Not Public
Sign In & Join Server To View
Tom
Tom2y ago
yeah i tried that. it kinda all broke down when i had an input component that had the job of building an array of objects sorry. just noticed you sent this days ago but once i was inside the subcomponent all info on the types was completely lost and react-hook-form has pretty mediocre support for types within field arrays
Want results from more Discord servers?
Add your server