How to "useImperativeHandle" hook in React

Yarden Hochman
4 min readJul 17, 2022

This piece is intended to explain when, and then how, you should use the hook useImperativeHandle. But first…

Is it bad practice?

Using the imperative handle hook reverses the control flow in React. This control flow keeps our code predictable. Parent components load child components, these child components can use parent components’ functions if those are passed to them, but parents cannot use children components’ functions. Therefore, imperative handle hook exists to allow edge cases in which the right thing to do is to leave the control in the child, and activate it in the parent.

This behavior already existed in React before the introduction of this hook. It was used when we wanted to control HTML methods.

focus is a method of HTML “input” elements. We control it with a ref

We also used refs when we had to deal with a library that didn’t comply with React’s model of thinking, and utilized class based instances or other such practices. Passing refs to these components allowed us access to that component’s class instance and methods. These cases are less related to this article though, so we’ll put it aside for now.

HTML-like JSX in forms — React hook form

Sometimes we might want to replicate the HTML ref control flow to React components. One such popular case is React hook form library control flow.

no state! the form just manages itself and submits to wherever when needed

The case this library makes is that when you build a form, instead of managing the state in React, we could make use of the state that already exists in HTML. In vanilla HTML, we can build a form and fill the input fields and the state is preserved inside the HTML. Replicating this state into React is both cumbersome, and redundant, since it’s going to exist in the HTML anyway!

So at my company we wrote a nice form using this library, which managed its own state, utilizing our specified validation requirements, with a much better dev experience as well as a significantly leaner JS and quicker response times for the client. Instant win right?

But then we ran into the following issue: what about cases where we need to control the form from a parent? We needed to find a way to save the form’s contents from a footer that was completely separate from it. How could we possibly trigger the handleSubmit when that method only existed within the form?

UseImperativeHandle for the rescue

This is in essence the logical conclusion of the optimization we’ve done earlier. Rather than control each field in the state as we would in the naive form implementation, we have created a “vanila-like” HTML form that takes care of itself with some JS on the side to align with our validation, and now just like an HTML element, we need a ref to expose this form’s instance and methods.

Lets break it down into steps.

  1. Add a ref element using useRef hook in the parent component that’s going to control the form element. Pass this ref to the child. Best Practice: specify what this ref is going to receive using Typescript!
Notice we can use Typescript to specify what the ref is going to contain.

2. To use refs in React Components rather than HTML DOM elements, we need to wrap the components the refs are passed to in a “forwardRef” function. We can specify here the type of the ref again, notice its value is “undefined” when the component initially loads.

Notice that you need to close the open parenthesis at the bottom of the component as well. ‘forwardRef’ is a High order component

3. Add the useImperativeHandle hook to expose it to the external component. Best Practice: Specify the type of the ref in the return function.

use the provided ref, and expose to it whichever functions you intend to use in the parent.

4.I can now safely use submitForm in the parent!

notice the question mark after “current”. This is because until ChildWithForm is loaded, this function is undefined.

Summary

React’s control flow approach of top to bottom is better for most uses cases, keeping our code predictable. When it comes to forms, there is a shift to using the opposite control flow, of bottom up, or HTML to React code. This is normally simple to do, but sometimes it means we need to implicitly control the form from above. This is a performance/readability trade off which we make to give users and developers a better, more native experience. The useImperativeHandle hook allows us to effortlessly take charge of these edge cases.

--

--