Glad Chinda Full-stack web developer learning new hacks one day at a time. Web technology enthusiast. Hacking stuffs @theflutterwave.

How to use React createRef

12 min read 3585

React-createref-DOM-featured-image

If you’ve been developing web applications long enough, then it is likely you’ve used JavaScript DOM libraries such as jQuery, Mootools, Prototype.js, etc.

The advent of these libraries brought about a major shift on how interactive web applications were built.

With DOM abstraction APIs, manipulating the contents of a web app became so much easier.

For example, you would find yourself doing something like this with jQuery:

$('#button').on('click', function(evt) {
  evt.preventDefault();
  var content = $('<h1>Random Post Title</h1><p>Random post text.</p>');
  
  $('#element').append(content);
});

Following the trends for the last couple of years, you also would have noticed that the focus is now on JavaScript frameworks like React, Angular, Ember.js, and Vue.js  for building modern day applications.

One similarity between these frameworks is that they are built with a component-based architecture. So these days, it is more common to hear about components than it is to hear about DOM-based interactions since most of those are encapsulated within the component.

While you can do a lot with these modern JavaScript frameworks by leveraging on their built-in functionalities, there are times you need to interact with the actual DOM for some native behavior. Most of the modern frameworks provide APIs through which you can access the native DOM representation of your app, and React is not an exception.

In this tutorial, we will consider how we can interact with the DOM in a React application. We will also see how we can use the React.createRef() feature introduced in React 16.3 and the useRef hook introduced in a later version of React.

What are React refs and DOM?

React provides a feature known as refs that allow for DOM access from components. You simply attach a ref to an element in your application to provide access to the element’s DOM from anywhere within your component.

Refs can also be used to provide direct access to React elements and not just DOM nodes. Here is what the React documentation says concerning refs:

We made a custom demo for .
No really. Click here to check it out.

Refs provide a way to access DOM nodes or React elements created in the render method.

Generally, the use of refs should be considered only when the required interaction cannot be achieved using the mechanisms of state and props.

However, there are a couple of cases where using a ref is appropriate. One of which is when integrating with third-party DOM libraries. Also, deep interactions such as handling text selections or managing media playback behavior also require the use of refs on the corresponding elements. You can check out our React reference guide to learn more.

Creating refs in React

React provides three major ways of creating refs. Here is a list of the different methods starting from the oldest of them:

  1. String refs (legacy method)
  2. Callback refs
  3. React.createRef (from React 16.3)
  4. The useRef Hook (from React 16.8)

String refs in React

The legacy way of creating refs in a React application is using string refs. This is the oldest method and is considered legacy or deprecated because it will be removed in future releases of React.

String refs are simply created by adding a ref prop to the desired element, passing a string name for the ref as its value. Here is a simple example:

class MyComponent extends React.Component {

  constructor(props) {
    super(props);
    this.toggleInputCase = this.toggleInputCase.bind(this);
    this.state = { uppercase: false };
  }
  
  toggleInputCase() {
    const isUpper = this.state.uppercase;
    
    // Accessing the ref using this.refs.inputField
    const value = this.refs.inputField.value;
    
    this.refs.inputField.value =
      isUpper
        ? value.toLowerCase()
        : value.toUpperCase();
        
    this.setState({ uppercase: !isUpper });
  }

  render() {
    return (
      <div>
        {/* Creating a string ref named: inputField */}
        <input type="text" ref="inputField" />
        
        <button type="button" onClick={this.toggleInputCase}>
          Toggle Case
        </button>
      </div>
    );
  }
  
}

Here we have created a simple React component that renders an <input> element and a <button> element that allows us to toggle the case of the input between uppercase and lowercase.

We initialized the state of the component with an uppercase property set to false. This property allows us determine the current case of the input.

The main emphasis is on the string ref we created on the <input> element. We also created a ref to the <input> element named inputField.

Later in the click event handler for the <button> we accessed the ref via this.refs.inputField. And manipulated the DOM of the <input> element to change the value of the input.

Here is a sample demo of what the interaction looks like:

string-refs-DOM-createRef
Sample demo screenshot

Although this is a very trivial example on how to use refs, we have been able to see how string refs can be used in a React component. As stated earlier, using string refs in your React application should be discouraged.

Here is what the React documentation says concerning string refs:

If you’re currently using this.refs.textInput to access refs, we recommend using either the callback pattern or the createRef API instead.

Using callback refs in React

Callback refs use a callback function for creating refs instead of passing the name of the ref as a string. If you are using versions of React earlier than version 16.3, then this should be your preferred method of creating refs.

The callback function receives the React component instance or HTML DOM element as its argument, which can be stored and accessed elsewhere. Using a callback ref, our previous code snippet will become the following.

class MyComponent extends React.Component {

  constructor(props) {
    super(props);
    this.toggleInputCase = this.toggleInputCase.bind(this);
    this.state = { uppercase: false };
  }
  
  toggleInputCase() {
    const isUpper = this.state.uppercase;
    
    // Accessing the ref using this.inputField
    const value = this.inputField.value;
    
    this.inputField.value =
      isUpper
        ? value.toLowerCase()
        : value.toUpperCase();
        
    this.setState({ uppercase: !isUpper });
  }

  render() {
    return (
      <div>
        {/* Creating a callback ref and storing it in this.inputField */}
        <input type="text" ref={elem => this.inputField = elem} />
        
        <button type="button" onClick={this.toggleInputCase}>
          Toggle Case
        </button>
      </div>
    );
  }
  
}

Here we have made two major changes. First we defined the ref using a callback function and storing it in this.inputField as follows:

<input type="text" ref={elem => this.inputField = elem} />

Then, in the event handler, we access the ref using this.inputField instead of this.refs.inputField.

When using inline callback refs like we did in our example, it is important to know that for every update to the component, the callback function is called twice — first with null, then again with the DOM element.

However, creating the callback function as a bound method of the component class can be used to avoid this behavior.

Using a callback function for creating refs can give you more control over how the ref is created, set and unset.

Using React.createRef

Starting from React 16.3, the React API included a createRef() method that can be used for creating refs in much the same way as we did using the callback function. You simply create a ref by calling React.createRef() and assign the resulting ref to an element.

Using React.createRef(), here is what our previous example will look like:

class MyComponent extends React.Component {

  constructor(props) {
    super(props);
    this.inputField = React.createRef();
    this.toggleInputCase = this.toggleInputCase.bind(this);
    this.state = { uppercase: false };
  }
  
  toggleInputCase() {
    const isUpper = this.state.uppercase;
    
    // Accessing the ref using this.inputField.current
    const value = this.inputField.current.value;
    
    this.inputField.current.value =
      isUpper
        ? value.toLowerCase()
        : value.toUpperCase();
        
    this.setState({ uppercase: !isUpper });
  }

  render() {
    return (
      <div>
        {/* Referencing the ref from this.inputField */}
        <input type="text" ref={this.inputField} />
        
        <button type="button" onClick={this.toggleInputCase}>
          Toggle Case
        </button>
      </div>
    );
  }
  
}

Here we see a couple of changes. First, in the constructor(), we created a ref using React.createRef() and stored it in this.inputField as follows:

this.inputField = React.createRef();

Next, in the event handler, we access the ref using this.inputField.current instead of this.inputField. This is worth noting for refs created with React.createRef(). The reference to the node becomes accessible at the current attribute of the ref.

Finally, we pass the ref to the <input> component as follows:

<input type="text" ref={this.inputField} />

We have explored the various methods of creating refs in our React application. In the following sections, we will go ahead and take a closer look at more interesting characteristics of React.createRef.

Using the React useRef Hook

With its release in React v16, the Hooks API has become the de facto means of abstracting and reusing code in React applications. One such Hook is useRef, which allows us to create and use refs in functional components.

Note that even with the useRef Hook you still, by default, cannot use the ref attribute on functional components because we cannot create instances of functions. We will discuss how to get around this with ref forwarding later on in this article.

To use the useRef Hook, you pass in the object that ref.current should refer to to the useRef Hook and call it. This Hook call should return a ref object that you can now use as if you were using the createRef method discussed earlier.

Here’s what our previous example should look like if we use the useRef Hook:

const MyComponent = () => {
    const [uppercase, setUppercase] = React.useState(false)
    const inputField = React.useRef(null)
    const toggleInputCase = () => {
        // Accessing the ref using inputField.current
        const value = inputField.current.value;
        inputField.current.value = uppercase ? value.toLowerCase() : value.toUpperCase();
        setUppercase(previousValue => !previousValue)
    }

    return(
       <div>
           {/* Referencing the ref from this.inputField */}
           <input type="text" ref={inputField} />
           <button type="button" onClick={toggleInputCase}>
                Toggle Case  
           </button>
       </div>

As you can see, the code is pretty similar to that of the React.createRef implementation. We create a ref using the useRef Hook and pass that ref to the ref attribute of the <input> HTML element.

For the <button> element’s event handler, the process is also the same as before. We update the value property of the HTML element that our ref points (which can be accessed by using ref.current) depending on the current value of the state variable, uppercase.

Creating component refs in React

In the previous section, we saw how we can create refs using the React.createRef API. The actual reference is stored in the current attribute of the ref.

In our examples so far, we have only created refs to DOM nodes in our application. But it is also possible to create refs to React components, which will give us access to the instance methods of such components.

Note that we can only create refs on class components since they create an instance of the class when mounted. Refs cannot be used on functional components.

Let’s consider a very simple example to demonstrate using refs on React components. We will create two components in this example:

  • FormInput component that simply wraps an <input> element and provides two methods. One for knowing when the input contains some value, and the other for selecting the input text.
  • MyComponent simply wraps the FormInput component and a button to select the text in the input when clicked.

Here are the code snippets for the components:

class FormInput extends React.Component {

  constructor(props) {
    super(props);
    this.textInput = React.createRef();
  }
  
  hasText() {
    return this.textInput.current.value.length > 0;
  }
  
  selectInputText() {
    this.textInput.current.select();
  }
  
  render() {
    return (
      <div>
        {/* Creating a string ref named: textInput */}
        &lt;input type="text" ref={this.textInput} />
      </div>
    );
  }
  
}

Like before, we created a ref using React.createRef() and added the ref to the <input> element in the render function. We created two methods:

  • hasText(), which returns a Boolean indicating that the input element‘s value is not empty. Hence, it returns false if the input is empty, otherwise it returns true.
  • selectInputText(), which makes a selection of the whole text in the input element.

Notice that we get a reference to the input element in our methods by accessing the current attribute of the ref we created, that is: this.textInput.current.

Now let’s go ahead and create MyComponent. Here is the code snippet:

const MyComponent = (props) => {

  const formInput = React.createRef();
  
  const inputSelection = () => {
    const input = formInput.current;
    
    if (input.hasText()) {
      input.selectInputText();
    }
  };
  
  return (
    <div>
      <button type="button" onClick={inputSelection}>
        Select Input
      </button>
      
      <FormInput ref={formInput} />
    </div>
  );
  
};

In this snippet, I use a functional component instead of a class component, as this is a stateless component. I also want to use this component to demonstrate how refs can be used inside functional components.

Here we create a ref using React.createRef() and store it in the formInput constant. Notice here that we are not using this, since functional components are not classes and hence do not create instances.

Notice in the render() method that we added the ref we created to the <FormInput> component we created earlier. Unlike the previous examples where we were adding refs to DOM nodes, here, we are adding a ref to a component.

Note that a ref can only be created for a class component and not a functional component. FormInput is a class component so we can create a reference to it. However, we can use a ref inside a functional component like we did in this example using formInput.

Finally, in the inputSelection() function, we access the reference to our component using the current attribute of the ref as before.

Notice that we are able to get access to the hasText() and selectInputText() methods of the FormInput component because the reference points to an instance of the FormInput component. This validates why refs cannot be created for functional components.

Here is a sample demo of what the interaction looks like:
React-createRef-form-component

Track state and user interaction with components

It’s important to validate that everything works in your production React app as expected. If you’re interested in monitoring and tracking issues related to components AND seeing how users interact with specific components, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web apps, recording literally everything that happens on your site. The LogRocket React plugin allows you to search for user sessions where the user clicks a specific component in your app. With LogRocket you can understand how users interact with components, and surface any errors related to components not rendering.

In addition, LogRocket logs all actions and state from your Redux stores. LogRocket instruments your app to record requests/responses with headers + bodies. It also records the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps. Modernize how you debug your React apps — .

Refs in uncontrolled components

By default, all components in React are controlled because React is responsible for handling updates to the component when form data changes.

When dealing with uncontrolled components in React, refs come in very handy. This is because instead of using event handlers to update state when form data changes, you rely on refs to get form values from the DOM. You can learn more about uncontrolled components here.

Let’s create a simple controlled component and then an uncontrolled component to demonstrate how we can use refs to get form values from the DOM.

class ControlledFormInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = { value: "Glad" };
  }
    
  handleChange(evt) {
    this.setState({ value: evt.target.value });
  }
  
  render() {
    return (
      <div>
        <h1>Hello {this.state.value}!</h1>
        <input type="text" value={this.state.value} onChange={this.handleChange} placeholder="Enter your name" />
      </div>
    )
  }
}

The above code snippet shows a controlled component that contains an <input> element. Notice that the value of the <input> element is gotten from the value property of the state. We initialized the value on the state to “Glad” (that’s my name anyway).

Also notice that we use the handleChange() event handler to update the value property in the state with the value of the input element we got from accessing evt.target.value.

Here is a sample demo of what the interaction looks like:

ref-controlled-component-react-demo
Controlled component demo.

Here is the uncontrolled version of the component:

class UncontrolledFormInput extends React.Component {
  constructor(props) {
    super(props);
    this.inputField = React.createRef();
    this.handleChange = this.handleChange.bind(this);
    this.state = { value: "Glad" };
  }

  handleChange(evt) {
    this.setState({ value: this.inputField.current.value });
  }

  render() {
    return (
      <div>
        <h1>Hello {this.state.value}!</h1>
        {/* Attach the created ref: this.inputField */}
        <input type="text" ref={this.inputField} defaultValue={this.state.value} onChange={this.handleChange} placeholder="Enter your name" />
      </div>
    )
  }
}

We have made a couple of changes here for the uncontrolled version of our previous component. First we create a ref using React.createRef() and store it in the this.inputField instance property. We also attach the ref to the <input> element in the render() method.

We also use the defaultValue prop to assign this.state.value as the default value of the <input> element — which is only useful up till when the input value is first changed.

If we had used the value prop to specify a default value for the input, then we will no longer get updated values from the input. This is because in React, the value prop automatically overrides the value in the DOM.

Finally, in our event handler we access the value of the input element using the ref instead of the event object. So we do this:

this.setState({ value: this.inputField.current.value });

Instead of doing this:

this.setState({ value: evt.target.value });

The demo is the same as with the controlled version. Here is a sample demo of what the interaction looks like:

react-createref-uncontrolled-component-demo
Uncontrolled component demo.

Ref forwarding in React

Ordinarily, React components cannot access the refs used in their children components. While, according to the React documentation, this is good as it prevents components from relying on each other’s refs, there are instances where you might need to have access to a child component’s DOM element.

For example, suppose you have a custom text field component that you use throughout your React app. In most cases, this text field component will render a standard HTML input text field with some customization, most likely through props.

const TextField = ({placeholder}) => {
return (
<input type: "text" placeholder={placeholder}. />
);
}

If we wanted to access the <input> element directly in a parent component of TextField to perform some function like selecting the text, there’s no way to do that by default. However, we could employ two methods to achieve this:

  • Turn TextField to a class component and create a ref in the TextField component that points to the <input> element. Because TextField is a class component, we can access its methods from a parent component. We can now create a method inside TextField that selects(highlights) the <input>‘s element text. This is similar to what we did earlier while discussing creating component refs.
  • Our second option is to create the ref object in our parent component and “forward” the ref to the <input> element of TextField. This is called ref forwarding.

Ref forwarding allows us to create a ref in a parent component and forward that ref to its children. This allows us to then interact with HTML elements within those children components on the DOM level.

Here’s how we would use ref forwarding to achieve our objective:

const Parent = () => {
    const inputRef = React.useRef(null);
    const selectInputText = (e) => {
        inputRef.current.select()
    }
    return (
        <div>
            <TextField ref = {inputRef} placeholder="I am a text field!"/>
            <button onClick={selectInputText}>Select Text</button>
        </div>
    );
}

As you can see, we created a ref object in Parent and passed it to the TextField component. In the TextField component, we used the forwardRef function, which receives a functional component as an argument and passes the ref as a second argument to it.

Then, we passed this ref to the ref attribute of our <input> element. The ref created in Parent now refers to this <input> element and can interact with it.

Note that even though we were able to pass refs to a functional component, this ref cannot be used to access functions and variables that are local to that functional component.

Lastly, refs can also be forwarded to class components, like so:

const ComponentB = React.forwardRef(({ placeholder }, ref) => {
    return (
         <ComponentC ref = {ref} />
    );
}
)

Conclusion

In this tutorial, we have considered various methods in which we can interact with the DOM in a React application. We’ve also seen how we can use the new React.createRef() method and useRef Hook introduced in React to simplify creating refs.

You also now know how we can use refs in uncontrolled components. You can refer to the React documentation to learn more about what you can do with refs.

Glad Chinda Full-stack web developer learning new hacks one day at a time. Web technology enthusiast. Hacking stuffs @theflutterwave.

4 Replies to “How to use React createRef”

  1. Pretty informative article on refs. I would suggest you update the article about creating refs with hooks, since its available with React 16.8.6 and above.

    1. Hi Abid,
      Thanks for pointing that out! We’ve updated the article to include information about refs with hooks.

  2. Definitely the most analytical and comprehensive article on this topic I’ve seen. The introduction provided just enough context to understand the bigger picture and the comparison with its alternative was very detailed and well-illustrated! Also, the inclusion of the different versions of refs, including the deprecated one, is much appreciated considering how fast the library changes.This should be the standard of writing technical articles!

Leave a Reply