Passing Handlers or Centralize them?
I am using react-hook-form to create invoices with multiple modals for adding invoice items, applying discounts, updating information, and more. Each modal modifies the form state and requires specific handlers.
Should I define all handlers in the parent component and pass them down to the child components, or should I create the handlers directly within the child components?
55 Replies
RHF exposes APIs like useFormContext, which are great for sharing form state across deeply nested components. You can combine this with Zustand or Context to handle specific actions (like adding items or applying discounts) without breaking RHF's state management.
@TechWitchTitan thanks for response
I am using useFormContext for all my Form-Components to make them less noisy and verbose, but can I use them to pass custom functions?
Yuppers. For example, if you use Zustand, you can basically add prototypes on your store hook to do different things and then hook them into the context. Write it once, use it anywhere.
hmm...extra lib for that...let me think
I use Zustand a lot, so that is typically my go-to, But you can use the general context to do the same thing. The main advantage is that you can give access to the stores/useFormContext at a higher level in the component tree
And for me, Zustand is perfect for carts what whatnot.
makes sense
At the moment I have some complexity that I try to manage and I do not know what strategy I should pick
for example I have an invoice form where you can add a lot of products and services
The adding logic is in a modal
Each item of your invoice has some other modals for giving discount and another modal for total discount
My Form has tabs, each tab is a file, each file as some children
at the moment I am using useFormContext in each child to manipulate the form state
Yeah, I would consolidate that into a useManageCart, and then all your modals/tabs have access
okay, now the thing is that each modal has its own state to be visible
should this also be handled in a centralized manner?
so modal state is "openModal"
and each modal has handler like when you click OK or Changel
Something like that would probably take me about 10min to setup. So a bit longer for someone who has never used it. When it comes to modals, I always have a useModalStore. That way I can close it from anywhere after any GET/POST's
let me try to get a bigger picture š
do you have all your form handlers in a centralized "area" aka file?
I am asking because this is what makes me a bit mad and is a bit uncomfortable:
"ohh I have to change this state and handler logic, where is it again, let me see"
For example: src/utils/stores/[slice]. Where slice is any use[slice]. From there you wrap the top level component with the provider. Sometimes that is global, sometimes that is for the page, sometimes its just for a group of components.
And if you wanna get super organized you can do something like: src/utils/[page/scope]/[slice]
feels very redux like
Zustand was to simplify context, and context was to simplify redux.
The boilerplate is super minimal
Redux would be easily 3 or 4 times as much as that for ref
but what I have so far is something like this:
Form.tsx contains react-hook-form
I would put that store in the Invoices folder, but let me see...
your zustand-store, is it not redundant because of the store of react-hook-form?
this is my component:
https://paste.xinu.at/uD8yG/jsx
Depending on how extensible you want it to be, yes, it can be redundant.
I will rename it from
SalesInvoiceItemsModal to
ModalAddNewSalesInvoice
let me ask you this first.
You saw my component?
For you, I wouldnt store any values in it, I would use it to implement some custom logic before you send it to RHF
it is a little abstract, but you do agree that all state and all handler should be centralized, right š ?
(please say yes)
Im not home, so im not gonna open links I am not able to vette
let me paste the code here then
I would agree that centralizing logic is my go-to approach when feasable.
I guess you want to make each child component as dumb as possible, is that correct š
dumb in sense of "consumes state and does not contain any state logic"
I do my best to keep the lowest child dumb af š¤£
I love that!
okay, next question
The rumor is that parents and grand parents are supposed to be the smarter ones.
sure! like in real life
if you look at my component, you would also centralize the state of this, right?
and give it a name like
isAddInvoiceItemModalOpen
Personally, yes, I would move that into the useModalStore. That way I can keep track of the multiple modals and ensure that I will always have access to their controls. For example, if you have it so that when you have a multi-step modal workflow, when you close one then you can open the next at the same time. And that isolates functionality and makes components independent.
From there, you can do all kinds of neat things.
And you can share the discount modal between your add new product, and add new service. If you have something like that
could we first start with a global store, that contains all the state of the Invoice Form?
You "could"
š¤£
okay, because I am a bit overwhelmed with the complexity and do not have a clear picture
Careful with that global term
correct, global was not correct, I mean in the Invoice-Form context š
Global implies giving the whole src access, when you really want to slice it up
yep, very good catch!
okay, did you see my folder/file structure?
I did
okay, just in case, I use this Form in a reusable manner
Which means I create one Form that I can use in my Create and Update routes
if there is data => update form
if there is no data => create form
in the end it is how the default state is set
nothing special
And that is where zustand comes in handy. Because you can create a slice for invoice/create, invoice/update, invoice/[insert new thing]
And then have invoice/[useStore] for anything that is shared between them
wait, this is still too advance for me and a little foggy and abstract, so lets look at this:
so we have our state for the Invoice there
but no UI form state, like toggle state for modals or something
in this Component, which is the parent component, there are also no handlers
how would you add zustand in this case?
(keep it very very very very very very simple so I can grasp the idea š )
and how do you decide what to put in RHF-state management and what to put in zustand
if I had to guess is:
All data that is for the backend => RHF state
Rest of data => zustand
In my "Add new Invoice Position" Modal-Component, I have something like this:
Here is a basic implementation of the modal store
use tsx after ```
for code highlighting š
did you read my questions š ?
let me know if you want to go to sleep or something
we can do it when you have the time š
You guess is right. UI control and middlware I would put in zustand, and then the end result values would be stored in RHF
Then when you are ready to update the DB, put that handler in the submit
thanks for this confidence boost š
That is what we are here for
okay, now there is one thing that I do not like
you created a
useModalStore
why not create a useInvoiceUIStore
?
WHY?
to put all the UI state that is UI specific AND to put there all handlerYou could do that if you wanted. But
useInvoiceUIStore
implies that you would put someting like tab control in there, when that is not something i would use Zustand forš¤
But that is just me
please correct my if I am wrong when ever you can, I realy want to hear your opinion š
so I would have for each form a monster store, that contains all the UI state AND handlers
WHY?
to have one centralized area to look into
Nah, you would put things in the store that could/would reach into multiple components.
You can think of it as this: If you would pass it down as a prop, then you can store it in zustand to make a quick hook.
nice rap!
let me think...
how does zustand communicates with RHF and uses its getters and setters for the state?
Something along these lines.
this is not centralized
what I expected is something like this:
And then if someone qualifies for a discount, then you can pop up that from this modal.
There are hundreds of ways it can be done. So take what I offer with grains of salt.
is something like this possible?
because what you have above is something what I have in a way
Let me think on that and i will get back to ya. I might try tinkering with it when I get back to my PC