A gentle introduction to Refs in React

By Precious Nana Ewusi Nyarko

In React, you generally should not be writing code that imperatively manipulates the DOM, but there are some situations where it’s necessary to do so. Refs exist, for just such occasions.

Refs provide a way access and manipulate DOM nodes and React elements , directly.

When it’s okay to use refs? Well, the react docs provide some guidance. According to the docs, you should consider using refs when you need to:

  • Manage focus, text selection, or media playback.
  • Perform imperative animations.
  • Integrate with third-party DOM libraries.

This list isn’t meant to be exhaustive. For example, managing canvas and media elements are other good use cases. Whatever the case may be, it is important that you assess your needs to determine if refs are a good fit. Just keep in mind that they are essentially an escape hatch — a last resort of sorts — so don’t make it your go to thing. Always try to do things declaratively first.

In general, you avoid using refs for things that can be done declaratively. An example is updating the background colour of a div, in response to a click event.

In this scenario, you should pass a backgroundColour prop to your component, instead of exposing a changeBackground() method (which you then call via access to a Ref) on it.

Alrighty! Now that we know when to use refs, let’s discuss how to use them.

You can create refs using React.createRef() and attach them to React elements via the ref attribute. Essentially, you assign the Ref returned from React.createRef() to an instance property, when a component is constructed (aka, in the component’s constructor). This way, the Ref can be referenced throughout the component.

Another way to set refs in React is to use callback refs. With this method, instead of passing a ref attribute created by createRef(), you pass a function that, when called, receives the React component instance or HTML DOM node as its argument, which can be stored and accessed elsewhere. Here’s what that looks like:

Example using callback refs

You may be wondering why we didn’t write the callback function line like so:

Ref callback as an inline function

Well, in most cases it shouldn’t matter, but, it’s useful to note that, React calls this function twice during updates, first with null and then again with the DOM node or React element. This is because a new instance of the function is created with each render, so React needs to clear the old ref and setup the new one.

Defining the ref callback as a bound method on the class, allows us avoid this behaviour.

We’ve talked of how the callback will get called with the appropriate element, but when exactly does this happen? Put differently, the question is, when can I expect that the ref, which in our example above, is inputRef will be set. Thankfully, the React docs are very clear on this:

React will call the ref callback with the DOM element when the component mounts, and call it with null when it unmounts. Refs are guaranteed to be up-to-date before componentDidMount or componentDidUpdate fires.

How do you access a ref’s underlying DOM node or React element? Well, that depends on how you created the ref.

React.createRef() returns an object with a current property.

The object returned by React.createRef()

You can retrieve the underlying DOM node or React element by accessing the object’s current property. You should note that, the underlying element will be a DOM node (if the ref was attached to an HTML element), or the mounted instance of the component (if the ref was attached to a custom class component).

Here’s an example to illustrate:

Accessing refs created with React.createRef()

Unlike the createRef() approach, when you a callback ref, the callback function you provide is called with the actual React component instance or HTML DOM node, as its argument.

Unlike when using createRef , we don’t receive an object with a current property. So you can store and access it directly without having to go through a current property to access it. Here’s an example:

Accessing callback refs

Remember how we said, that, when attached to your custom components, a ref’s current property receives the mounted instance of that component? Well, for functional components, this means that attaching a ref via the ref attribute won’t work, because functional components are, well, just functions and so there won’t be an instance to work with. The example below attempts to attach a ref to a functional component.

Attaching a `ref` to a functional component won’t work as expected

If you need a ref to a functional component, you should change the component to a class, just like you do when you need lifecycle methods or state.

In rare cases, you might want to access a child’s DOM node from a parent component. While not recommended, because it breaks component encapsulation, but it can occasionally be useful for triggering focus or measuring the size or position of a child DOM node.

If you find yourself in this position, you’ve got a few options depending on which version of React you’re on.

If you use React 16.3 or higher, it is recommended that you use ref forwarding — a technique for automatically passing a ref through a component to one of its children. This feature essentially allows a component to take a ref they receive, and pass it further down to a child.

It usesReact.forwardRef , which accepts a render function that receives props and ref parameters and returns a React node. In the example below, RedButton uses React.forwardRef to obtain the ref passed to it, and then forwards it to the DOM button that it renders:

Example using Ref forwarding

The key thing to note here is, if you pass a ref to a HOC it will not get passed through to the wrapped component. Instead of referring to the wrapped component, the ref will refer to the outermost container component. This is because when React encounters a ref attribute on a component, it attaches the ref to the HTML element or component on which the attribute was set. This is not what we want. Although we are setting the ref on the HOC, we really want the ref to get forwarded down to the wrapped component, instead of being attached to the HOC.

Fortunately, we can explicitly forward refs to the wrapped component using the React.forwardRef API. For example:

Using ref forwarding with HOCs

React 16.2 and earlier doesn’t yet support ref forwarding. If you need to expose a DOM ref to components above and can’t migrate to React 16.3+ yet, you can use the following approach instead.

You pass the ref to the child component as a differently named prop — really any name other than ref (e.g. buttonRef ) . The child component can then forward the prop to the DOM node via the ref attribute. This lets the parent pass its ref to the child’s DOM node through the component in the middle. This works both for classes and for functional components. Here’s what that looks like:

In the example above, Parent passes its class property this.inputElement as an inputRef prop to the CustomTextInput, and the CustomTextInput passes the same ref as a special ref attribute to the <input>. As a result, this.inputElement.current in Parent will be set to the DOM node corresponding to the <input> element in the CustomTextInput.

Note that the name of the inputRef prop in the above example has no special meaning, as it is a regular component prop. However, using the ref attribute on the <input> itself is important, as it tells React to attach a ref to its DOM node.

Refs offer a great way to pass data down to a child instance, outside outside of the typical data flow. While really come in handy for situations where you really need to access and manipulate DOM elements, you should be careful not to overuse refs.

You should bear in mind that, refs manipulate the actual DOM as opposed to virtual DOM which is contradictory to the React ethos. So use them sparingly.

Hope this post has left you with a good understanding of refs along with its use-cases and caveats. If you found this post useful, do share it with good peeps around you. Cheers!