Hello Sunil
react-useref-hook-feature-image

A Gentle Introduction to Refs in React (useRef Hook)

In this tutorial, you will learn how to get access to the actual DOM in React using so-called refs.

To do that, we will take a look at what refs are and when and how to use them.

Let’s get started!

What are Refs in React

When working with React, we are typically manipulating the DOM (Document Object Model) indirectly using the JSX syntax but there might be a situation where you may have to manipulate or imperatively modify DOM element.

An example would be auto-focusing a text box when a component renders. React doesn’t provide an easy way to do this, so we can use refs to access the DOM directly and focus the text box whenever the component renders on the screen.

So a refs in React provides a way to directly access a particular DOM element

In simple terms, refs give access to the underlying DOM element.

Refs are commonly used to implement Animations or when using legacy libraries like JQuery that require direct DOM-access. Also focus, text-selection and media playback are great examples for valid use cases.

When Not to Use Refs

The main reason to use React is to not have to deal with the DOM. Instead, we want React to handle all the DOM manipulation for us.

Using refs excessively is defeating that purpose. Manipulating the DOM directly should be considered a last resort.

So in general you should avoid using refs for things that can be done declaratively.

How to Use React useRef

To use refs in React, we usually follow these 3 steps:

Step 1: Import useRef from React.

import React, { useRef } from 'react';

Step 2: Then, declare and initialize the ref. You initialize a useRef() hook by passing an initial value to it or initializing it empty and updating its value later.

// Example - 1
const exampleRef = useRef();

// Example - 2
const inputElement = useRef(null);

Step 3: Access the DOM element with variableName.current.

useRef() stores an object that contains an attribute called current, which stores the passed value, in our example, it would hold the value 1.

const testRef = useRef(1)
testRef = { current: 1 }

Let’s learn better from an example.

ReactRefs.jsx

// 1. Importing useRef from React
import React, { useRef } from 'react';

const ReactRefs = () => {
  // 2. Creating firstParagraph and secondParagraph
  // and setting their values to useRef()
  let firstParagraph = useRef();
  let secondParagraph = useRef();

  // 3. Placing a ref attribute "ref={}" on both
  // paragraph tags, with the appropriate
  // variable as the attribute value
  return (
    <div>
      <p ref={firstParagraph}>This is first paragraph...</p>
      <button onClick={() => (firstParagraph.current.innerText = 'Hi')}>
        Change First Paragraph Text
      </button>

      <p ref={secondParagraph}>This is second paragraph...</p>
      <button
        onClick={() => (secondParagraph.current.style.backgroundColor = 'lime')}
      >
        Change Second Paragraph Background Color
      </button>
    </div>
  );
};


export default ReactRefs;

App.jsx

import ReactRefs from './ReactRefs';

function App() {
  return (
    <div>
      <ReactRefs />
    </div>
  );
}

export default App;

Output:

react-refs-simple-example

Kindly notice that within our onClick event we reference the appropriate ref which is holding our DOM element via variableName.current, we then manipulate the DOM element just as we normally would in Vanilla JavaScript.

Caveats of using useRef

Some important things to keep in mind when using useRef are:

A ref changing value doesn’t trigger a re-render

This one is often a tricky one, and trips a lot of developers! It is the opposite behavior of what happens when using useState.

📣 Also Read: useState in React: A Complete Guide

For example, the following code has a bug! Can you spot where it is?

RefsAndUseState.jsx

import React, { useRef } from 'react';

const ReactRefs = () => {
  // create a ref
  const counter = useRef(0);

  // increase the counter by one
  const handleIncreaseCounter = () => {
    counter.current = counter.current + 1;
  };

  return (
    <div>
      <h1>Learn about useRef!</h1>
      <h2>Value: {counter.current}</h2>
      <button onClick={handleIncreaseCounter}>Increase counter</button>
    </div>
  );
};


export default ReactRefs;

App.jsx

import ReactRefs from './RefsAndUseState';

function App() {
  return (
    <div>
      <RefsAndUseState />
    </div>
  );
}

export default App;

Output:

refs-does-not-re-render-automatically

Have you spotted it? If so, good job! The issue is that clicking the button increases the variable counter as expected, but it doesn’t trigger a re-render so we see nothing on the screen!

useState is best suited for data that affects how the user interface is rendered. useRef, on the other hand, excels at managing DOM manipulation and storing data for non-rendering purposes.

Although useState and useRef hooks involve data storage, their behaviour and application diverge notably:

Re-rendering: The useState hook causes a re-render whenever changes are made to the stored data. useRef, on the other hand, does not prompt re-renders, making it perfect for the storage of data unrelated to the user interface.

Data Mutation: Direct alteration of the stored state is prohibited in the context of useState; instead, the setter function must be used. In contrast, useRef allows for direct modification of stored values. useRef returns an object with a single property called current.

Value Access: The stored state variable must be accessed in order to retrieve data from useState. In the case of useRef, the current property provides access to the stored value.

It is useless to add a ref to a dependency array

Adding a ref to a dependency array for example in useEffect hook will not trigger the callback! This is also a very common error.

📣 Also Read: The React useEffect Hook for Absolute Beginners

For example, in the following example, you can click on the button all you want and it won’t print anything to the console!

ReactRefs.jsx

import { useEffect, useRef } from 'react';

const ReactRefs = () => {
  // create a ref
  const counter = useRef(0);

  // increase the counter by one
  const handleIncreaseCounter = () => {
    counter.current = counter.current + 1;
  };

  useEffect(() => {
    console.log('counter changed to: ', counter.current);
  }, [counter]);

  return (
    <div>
      <h1>Learn about useRef!</h1>
      <h2>Value: {counter.current}</h2>
      <button onClick={handleIncreaseCounter}>Increase counter</button>
    </div>
  );
};

export default ReactRefs;

App.jsx

import ReactRefs from './ReactRefs';

function App() {
  return (
    <div>
      <ReactRefs />
    </div>
  );
}

export default App;

Output:

useref-and-useeffect-hook

To fix both of these bugs, you should use useState instead of useRef:

ReactRefs.jsx

import { useEffect, useState } from 'react';

const ReactRefs = () => {
  // create a counter
  const [counter, setCounter] = useState(0);

  // increase the counter by one
  const handleIncreaseCounter = () => {
    setCounter(counter + 1);
  };

  useEffect(() => {
    console.log('counter changed to: ', counter);
  }, [counter]);

  return (
    <div>
      <h1>Learn about useRef!</h1>
      <h2>Value: {counter}</h2>
      <button onClick={handleIncreaseCounter}>Increase counter</button>
    </div>
  );
};

export default ReactRefs;

App.jsx

import ReactRefs from './ReactRefs';

function App() {
  return (
    <div>
      <ReactRefs />
    </div>
  );
}

export default App;

Now it works!

createRef vs useRef

useRef is the hook to create refs in functional components, but you can also use refs in the class components (Out dated now). The way you do it is by using the createRef function. 

The usage is very similar to useRef:

import { Component, createRef } from 'react';

class YourComponent extends Component {
  constructor(props) {
    super(props);
    this.yourRef = createRef();
  }
  render() {
    return <div ref={this.yourRef} />;
  }
}

Conclusion

In this tutorial, we learned how to use refs to manipulate DOM elements and components directly.

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.

Thanks for reading!

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

We are sorry that this post was not useful for you!

Let us improve this post!

Tell us how we can improve this post?

Similar articles you may like

Sunil Pradhan

Hi there 👋 I am a front-end developer passionate about cutting-edge, semantic, pixel-perfect design. Writing helps me to understand things better.

Add comment

Stay Updated

Want to be notified when our article is published? Enter your email address below to be the first to know.

Sunil Pradhan

Hi there 👋 I am a front-end developer passionate about cutting-edge, semantic, pixel-perfect design. Writing helps me to understand things better.