
1. Wait wait...what is it?
useImperativeHandle
allows us topass values and functions from aChild component to aParent using aref
.
From there, the Parent can either use it itself or pass it to another Child.
Note that you can only pass a ref as a prop to a child that wraps its component in
forwardRef
.
Code examples are much better than words when it comes to understanding so here is one:
// Parent ComponentconstApp=()=>{constref=useRef();return(<div><ComponentWithButtonref={ref}/><buttononClick={()=>ref.current.increment()}>another button</button></div>);};// Child ComponentconstComponentWithButton=forwardRef((props,ref)=>{useImperativeHandle(ref,()=>({increment}))const[count,setCount]=useState(0);constincrement=()=>setCount(count+1);return(<div><buttononClick={increment}>click</button><h2>Count:{count}</h2></div>)})
In the example above, we are changing the count variable in the parent component with the help ofuseImperativeHandle
andforwardRef
.
2. Why?
The general pattern in React is to have aunidirectional flow of data.
In cases wherebidirectional dataflow is needed, we can use libraries such asRedux
orReact context
.
However, in some cases, using those is simply just overkill.
This is whereuseImperativeHandle
comes in.
Now that we have some understanding of the hook and when we want to use it let's move to the Real Life Example...
We have aSettings
page that allows the user to update and edit his information and notification preferences.
The component hassections
and every section is a form that is responsible for changing data related to the user (A section for his profile info, his privacy settings, and his notifications settings).
constSection=({name,text,fields,schema})=>{const{control,handleSubmit,reset,formState}=useForm({mode:'onChange',defaultValues:fields.reduce((acc,field)=>({...acc,[field.name]:field.defaultValue}),{})});return(<sectionclassName={styles.section}><Titletext={text}/><formonSubmit={handleSubmit(onSubmit)}>{fields.map(field=>(<Fieldkey={field.name}{...field}control={control}/>))}</form></section>);};
Everysection
is rendered in theSettings
component - The Parent Component:
constSettings=()=>(<mainclassName={styles.main}>{SECTIONS.map(section=>(<Sectionkey={section.name}{...section}/>))}</main>);
Supposedly, everything is fine a Parent component that renders children...but what happens when we want to trigger the submit function of every section by clicking a global button?
We will need some way to allow the parent to control, that's whereuseImperativeHandle
comes in.
We will add the hook in theSection
component and wrap it with the forward ref so that we can pass a ref fromSettings
:
constSection=React.forwardRef(({name,text,fields,schema},ref)=>{const{control,handleSubmit,reset,formState}=useForm({mode:'onChange',defaultValues:fields.reduce((acc,field)=>({...acc,[field.name]:field.defaultValue}),{})});useImperativeHandle(ref,()=>({submit(){handleSubmit(onSubmit)();}}));return(<sectionclassName={styles.section}><Titletext={text}/><formonSubmit={handleSubmit(onSubmit)}>{fields.map(field=>(<Fieldkey={field.name}{...field}control={control}/>))}</form></section>);});
With the help of the hook we are able to create some sort of an API for the parent to use, in this example we are exposing thesubmit()
function that we we'll be able to call.
Now ourSettings
component will look like so:
constSettings=()=>{constrefProfile=useRef();constrefNotifications=useRef();constrefPrivacy=useRef();// The SECTIONS object is a configuration object that will// hold the refs among the rest of the dataconstonSubmitAll=()=>{SECTIONS.forEach(({ref})=>{ref.current.submit();});};return(<mainclassName={styles.main}>{SECTIONS.map(section=>(// The ref of each section i passed here in the spread// operation.<Sectionkey={section.name}{...section}/>))}</main>);}
That's it! We did it!
We Passed the control back to the parent without importing or using a more complex library.
3. Conclusion
I don't mean to disappoint but React doesn’t recommend using this hook. (there will most likely be another way in which you can do this without using the hook.)
Full disclosure, I ended up changing the component structure
But! nonetheless, it was super fun learning about this mysterious hook that is rarely used.
I hope you had fun too 🙏🏼 Thank you for reading!!
Top comments(6)

I don't quite agree with your comparison of Ref and Redux. Because my understanding is that Ref is more to operate on the HTML DOM.

Yes usuallyuseRef
is used for DOM nodes but in this case (with the help offorwardRef
anduseImerativeHandle
) we are able to create some sort of an API the parent can use, much like the global functions and state Redux gives us.
If you want to read more I found this link:reactjs.org/docs/refs-and-the-dom....

- LocationIsrael
- EducationM. Sc.
- WorkFront end develper
- Joined
Great read!
Even though it is advised against and I will probably not use it in my career. Reading of your journey with the task was nice and very relatable :)

- LocationBelo Horizonte
- WorkFullstack developer at Dti Digital
- Joined
Can you show a typescript'sref
correctly typed foruseImperativeHandle
?

Theref
's type will be:React.Ref<{ submit(): void }>
for more information check out this site:react-typescript-cheatsheet.netlif...
For further actions, you may consider blocking this person and/orreporting abuse