Handling external and internal refs to the same element with useImperativeHandle in React

Assaf Packin
Vimeo Engineering Blog
3 min readJul 22, 2020

At Vimeo we’re big fans of React, which renders our front-end components in a modular and reusable fashion.

React’s ref attribute is used to access components’ children or DOM elements. We normally use ref with React’s provided Higher Order Component (HOC) forwardRef and the useRef hook, but it can be confusing to share the ref attribute when using both of these methods at the same time.

This post describes how to use ref for each of these cases, and how we use the useImperativeHandle hook to resolve the conflict that arises when trying to use them both in the same component. This post is most relevant to React users working on systems for sharing components across products.

Passing external refs with forwardRef

We often use the Higher Order Component (HOC) forwardRef in design systems to provide consumers of components with references to the meaningful elements in them.

For example, in the following component:

We’ve decided that the input element is meaningful because we want to interact with its value from a parent component of FancyTextSubmit. To enable consumers of FancyTextSubmit to interact with the input element, we use forwardRef like so:

With this HOC, adding a ref to FancyTextSubmit grants access to its internal input element. This way, users can easily interact with the nested input element anytime they use FancyTextSubmit:

See this code in action on CodeSandbox.

Note that in this example we use ref to capture the value of the input when the user clicks the submit button. Typically when dealing with an input element in React, the first approach is to update the state from the input onChange. In that case, our code would look more like:

By using ref, we avoid unnecessary renders in situations that don’t require the intermediate values.

Accessing internal refs with useRef

Occasionally we also need a reference to the input within the FancyTextSubmit component itself. For instance, if we want to set the focus back to the input when a user clicks the submit button:

The problem

Our issue here becomes that we are already using the ref property of <input>. We need a way to supply both the inputEl ref and the forwarded ref.

The solution

Enter useImperativeHandle. This hook allows the sharing of the ref between both useRef and forwardRef:

See CodeSandbox for this example.

--

--