A better way of using refs in React

Ceci García García
Trabe
Published in
2 min readFeb 26, 2018

--

Although the common way for a React component to interact with a child is by re-rendering it with new props, sometimes it could be necessary to imperatively modify it; for example to access custom components API, manipulate the DOM to do some animation, handle uncontrolled components, integrate non-react stuff, etc.

React provides a way to do this imperative stuff through the especial attribute ref:

At mount time, the ref callback will receive the DOM element (in case you want to store an HTML element) or the mounted instance of the component (in case you want to store a custom component declared as a class).

The broken ref

So far, this approach is enough to deal with simple React components but, what would happen if the <CustomComponent /> needed to be wrapped?

ref is a special attribute so it won’t be passed through to the wrapped component as the rest of the props. We just lost the ref to the <CustomComponent /> so we cannot access its API anymore.

The most common solutions to this problem are based on adapting the wrappers to ensure the access to the component’s API:

  • Transparent ref delegation: The wrapper proxifies the component’s API exposing methods that just delegate the implementation to the wrapped component.
  • Explicit ref delegation: The wrapper component is in charge of setting the ref attribute to the inner component with the callback received in a known prop, in the following example componentRef.

Although this solution requires much less boilerplate code than the previous one, it still requires the adaptation of its closest wrapper. The wrapper adaptation involves two main problems:

  • Code maintenance: Both component and wrapper are coupled, which means that if a new wrapper is added, the old closest wrapper and the new one must be adapted.
  • External libraries: If the closest wrapper of the component belongs to an external library, we cannot adapt it without keeping a local modified copy of the library.

Implicit ref delegation

This solution comes from trying to get the wrappers to be totally agnostic about the ref delegation, on the basis of the previous solution.

The implicit ref delegation consists in emulating the React refs mechanism through a new special prop (innerRef in the example bellow). This way, the callback can flow transparently through the wrappers leaving the responsibility to finally set the ref to the component itself:

This way, we only have to adapt the component itself to allow the use of its API methods. No boilerplate, easy maintenance and no problem dealing with external libraries (as long as they let the props pass to the wrapped component 😅).

--

--