Throw the Flag: Reacts createRef API, callbackRef API, and forwardRef API

Steven Accardo
9 min readMay 16, 2018

--

An overview of the React createRef API callbackRef API, and forward Ref API as of version 16.3.

If you have ever played or watched sports, you’ve probably got upset at a ref or two in your time. If so, you’ll be happy to hear that’s not the ref we are going to be talking about in this article. We’ll instead be discussing React refs, short for references, and how they are used to interact with the DOM.

We’ll do a quick overview of refs, how they have been used in the past, and what React’s v16.3 brings to the table.

Ref Overview

If you are just learning React, then you probably have watched a few tutorials or read a few articles that tell you “Whatever you do, DO NOT touch the DOM”. Sound familiar? I know have had it drilled into my head! That’s because React utilizes a virtual DOM, and we always want the virtual DOM and the actual DOM to be in sync, otherwise things start to get coocoo for Coco Puffs. For the most part, this is true. That’s also why we generally are told not to use libraries like jQuery which cut out React and work directly on the DOM themselves. By using React, we have allowed it to be the middleman for all of our DOM transactions, and if there is anything you have picked up from all of those crime shows that you watch on Netflix, its that the middleman doesn’t like to be cut out…...its bad for business.

So Why Are We Here?!?

Well, you being Neo from The Matrix, reincarnate, don’t like rules. You dodge bullets, kill evil programs, date hot chicks in leather body suits, and mess with the DOM directly when ever you feel the need, right?!?! Well, yes….sometimes.

There are certain cases where you may want/have to interact with the DOM directly. Some of these cases are as follows:

  • When you want to manage the focus of inputs, grab the value of some text selection, or interact with different media playback options
  • When you want to work with animations
  • When interacting with third-party libraries such as Google Maps

So when you MUST access the DOM, you do so by making use of the ref API. Refs allow you to access the DOM nodes by passing a ref attribute to an element or component instance, much like you would pass a prop to a child component.

Always be careful when working with the DOM directly, we don’t want to piss off the middleman, even if you are Neo!

Refs in the Past

Refs aren’t new to React, what has changed is the way they are used. First I want to discuss how refs were used before v16.3.

There were two different APIs being used for refs in the past, the stringRef API and the callbackRef API. The string version was simpler to use, and more elegant to read, but it came with a list of issues.

Once those issues were discovered, React recommended the callback version as the best use case, which has been the standard up until now. As you may be aware callbacks are not the prettiest to look at, and while you do get used to using/seeing them, simpler options are usually easier on the eyes. We’ll discuss the new API a bit later

First, I want to show you some code snippets for a simple example using the callbackRef API, and then I will show you the same example using the new createRef API and forwardRef API.

In the list above I mention managing the focus of inputs is one use case where refs come in handy. So, below I have setup some examples where we will accomplish that in different scenarios using the different ref APIs.

You will see two inputs and a submit button, much like you would see on a login page or modal. We want the user to be able to type their value into the email input, press the enter key, and then have the focus shift from the email input to the password input without the user having to click on the password input or press the tab key. Similarly, we want the user to do the same to shift the focus down to the submit button from the password input. Finally, hitting enter again will call our onClick handler where we can dispatch an action or make a POST request with the user’s values.

callbackRef API Use Case in a Class Component

Below I have setup a Login class which extends React.Component. In the constructor method, I create three properties on the component instance, and set them all to null. There is an onClick method which is invoked when the submit button is clicked. There is an onKeyUp method which is invoked whenever a key is released. The render method returns the two inputs and the submit button. For each of those three, you will see a ref attribute. That ref attribute contains a callback which has the actual elements passed into them. You can call the parameter whatever you want, I called it DOMNode. Then in each callback the element node is stored on the Login instance for later use. The callbacks are invoked before the component is mounted or updated, so you will have access to the DOM nodes in the componentDidMount and componetDidUpdate lifecycle methods, FYI. You can learn more about React’s lifecycle methods in another one of my articles.

Note: I know the whole passing of the DOMNode into the ref which sits on the elements seems a little weird. This is how I think about it:

  1. Before the component is mounted or updated, React looks for any refs.
  2. If refs are found, it will then execute the code inside of the refs, which in this case is our callback functions. When the callback is invoked, React passes in the HTML element (in this case) into the callback as an argument. Wherever the ref attribute sits, whether on an element or component instance, that will be what is passed into the callback.
  3. That element is then assigned to the corresponding property and replaces the null value that we set in the constructor.
  4. Now the component is mounted and the property allows you to reference that specific element node within the component instance.

Each input has a onKeyUp handler which calls our method. So that we can use one method for each input, I bind the method and pass in a string identifying the input as an argument. When a key is released, we check to see if it originated from the user pressing enter, then we hit a switch statement which checks the target argument we passed in. The appropriate DOM node will be accessed, and the focus method will be invoked on it. This will equate to the focus shifting on each press of the enter button, just like we wanted!

createRef API in a Class Component

Remember how earlier I said that the string ref API was simpler, and more elegant, but had a list of issues? Well, the createRef API allows us to forgo the callbacks as well.

The code below is accomplishing the same as above except we are using the createRef API, and its corresponding syntax instead.

The Differences from the callbackRef API:

  1. We import createRef
  2. Instead of setting the instance properties to null, we set them equal to the createRef function.
  3. In the render method, notice that the callback functions are gone and have been replaced by the instance properties that we set earlier in the constructor.
  4. Lastly, in the onKeyUp method, you will notice we have added the current property when accessing the DOM nodes. That is because the elements that are passed into the instance properties are placed on the current property. Component instances are also accessed on this property as well.

callbackRef API Use Case in a Functional Component

Now onto refs in functional components. Notice, how I say in and not on? More on that in a bit, pun intended.

We can accomplish the same results that we did above, but now with functional components by making some changes to our code.

The Differences from the Class Component Version:

  1. This is no longer a class, so there is no “this” keyword to reference a component instance.
  2. We no longer use the bind method in the onKeyUp events. we now us an arrow function that invokes our event handler and explicitly passes it the target value and the event object.

createRef API Use Case in a Functional Component

The Differences from the callbackRef API:

  1. We import createRef
  2. Instead of setting the instance properties to null, we set them equal to the createRef method.
  3. In the render method, notice that the callback functions are gone and have been replaced by the instance properties that we set earlier in the constructor.
  4. Lastly, in the onKeyUp method, you will notice we have added the current property when accessing the DOM nodes. That is because the elements that are passed into the instance properties are placed on the current property. Component instances are also accessed on this property as well.

Earlier, I mentioned there was a difference between using refs in and on functional components. Let’s go over that difference now.

Refs can be used in a functional component, as you have seen above, but they can not be used on a functional component. Cool, so what the heck does that mean??

Imagine you have a functional component, we’ll call it FancyInputComponent, and within that functional component is rendered a simple input field. As we know, you can use refs on elements and component instances. Now, let’s say you wanted to reference that component, but not the actual input that the component wrapped. You might try to add the ref attribute to the component, so that you can reference it at some point in your application. However, functional components do not have instances, like class components do. So this will not work. Instance is the key word there. So, if you want to reference a component itself, and not the elements within it, then you will need to turn that functional component into a class component.

This will not work.

callbackRef API Use Case Passing a Ref to A Child Component

There may be times when you want a parent component to be able t o reference the DOM Node of an element within one of the parent’s child components. Ideally, React would like you to not do this, as it breaks component encapsulation, but you are Neo, so let’s check it out!

Below we have two functional components. We want to give the Parent component access to the input element that is wrapped within the FancyInputComponent. This way when the user clicks the button on our Parent component, we can focus on the input element. In order to do this, we have to be able to reference that input.

Since we want to reference the input element and not the functional component that wraps it (we can’t do that anyway, remember?), we have to pass the ref callback as a prop, which I have named inputRef. The FancyInputComponent makes the callback available, and then we set the ref of the input element equal to the passed down callback. Finally, when the user clicks the button, the inputFocus function is invoked. The function grabs the value stored in textInput, which is our input element node, and invokes the focus method on it, which yields the proper result.

createRef API Use Case Passing a Ref to A Child Component

The Differences:

  1. We set the textInput value to createRef()
  2. In the inputFocus we are accessing the DOM node on the current property

forwardRef API Use Case Passing a Ref to A Child Component

Here we use the both the createRef API and the forwardRef API to accomplish the same thing.

The Differences:

  1. We import forwardRef
  2. Instead of passing the inputRef attribute, we are able to pass ref
  3. We use forwardRef to encapsulate the entire component, and we pass it props and then the ref attribute
  4. We set the ref attribute on the input element equal to the passed in ref.

Note: You don’t have to use the forwardRef API to pass refs to children. Any of the 3 options above are valid depending on what you prefer.

Another Note: While createRef API has its benefits, the callbackRef API is not being deprecated and may be preferred in certain situations. So use either, however you would like.

Refs can be used in multiple ways, but hopefully this has given you a basic overview and some common use cases on how they can be utilized.

Thanks for reading!

--

--