useRef hook

useRef hook

In this article, we shall discuss what is the useRef hook. We will then understand the use cases of this hook with the help of examples. Then we will discuss when to use useRef. We shall also compare it with useState, another well-known hook.

go-b99.gif

What is useRef?

useRef is a React hook which stores the reference and helps us in persisting value between renders. It returns the value in an object and the value can be accessed via the 'current' key. This value can be used during the whole lifecycle of the component. We shall see more about this in the below sections.

There are two ways to define a ref -

  1. Without any argument - The value of the ref is undefined until it's assigned to an element.

    const divRef = useRef()
    
  2. With an argument - The useRef hook can take a maximum of one argument which will be the initial value of the ref.

    const countRef = useRef(0)
    

Use cases of useRef

Primarily the useRef hook has two main uses -

  • It is used to access a DOM child directly.
  • It can be used to store a mutable value that does not cause a re-render when updated.

Accessing a DOM element

function App() {
  const divRef = useRef();

  useEffect(() => {
    console.log(divRef.current);
    /* Output will be-
        <div class="App">
           <h1>This is a useRef example</h1>
        </div> */
  }, []);

  return (
    <div className="App" ref={divRef}>
      <h1>This is a useRef example</h1>
    </div>
  );
}

This can be used for DOM manipulation. In Vanilla Javascript, we can access the elements using querySelector, but we can't do this in React directly. We need to use the useRef hook to achieve this functionality. For instance -

useEffect(() => {
    divRef.current.style.color = "red";
  }, []);

This changes the color of the text in the div to red.

Storing values in ref

Consider the below example -

function App() {
  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>
  );
}

Now try running this code in your editor. Did you see any update? No, right? That's weird, we are updating the ref value, then why isn't the value getting updated?

laugh-brooklyn.gif

The answer to the above question is because useRef doesn't cause the screen to re-render. Try consoling the value of the counter in your browser and you'll see that the value is getting updated, but since the screen isn't re-rendering, it isn't displayed on the screen.

The second use case. In the below example, the previousInputValue ref stores the previous value of the input.

function App() {
  const [inputValue, setInputValue] = useState("");
  const previousInputValue = useRef("");

  useEffect(() => {
    previousInputValue.current = inputValue;
  }, [inputValue]);

  return (
    <div>
      <input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
      />
      <h2>Current Value: {inputValue}</h2>
      <h2>Previous Value: {previousInputValue.current}</h2>
    </div>
  );
}

Here we see the value being updated on the screen, this is because of the useState hook which causes the state update when the value is changed. Note- Updating a ref is a side effect so it should be done only inside a useEffect or inside an event handler.

When to use useRef?

  • when you want to store the reference of a particular DOM element. A common use case for this is when using modals. When the user clicks outside the modal, it should close. This can be achieved by storing the reference of the modal element.
  • can be used for SEO purposes where the developer wants to check how many times a particular button was clicked by one user.
  • can be used when some value has to be computed in the code, which doesn't need to be rendered on the screen.

useRef vs useState

A ref whose value is changed doesn't cause the component to re-render. This is the complete opposite of what useState does. useState causes the component to re-render even if there's a small change in the state value. If you want to persist state across re-renders of your components you should be using useState instead of useRef. A good practice is defaulting to useState, and then only using useRef if you have a specific reason to do so.

Using the useRef hook incorrectly can cause a lot of issues and hence it's important to know when to use this and when not to. A lot of developers misuse this hook. For instance, when you start writing the code, it may seem fine to use useRef if you don't want the component to re-render because of the value change. But later there might be a case where there is a need and then the developer goes all crazy since they see the value is getting updated when logging it but not on the screen. Hence, it is important to use useRef only when it's completely necessary or for accessing DOM elements.

Why local variables can't replace useRef?

The answer to this question is pretty simple. If you declare a variable using let/var and if you reload the screen, the variable will be initialized to the original value with which it was declared. i.e. the value isn't persistent. In the case of useState, useRef, React internally takes the previous value stored from the store and re-initializes the states to the last value. This is not the case with local variables and hence can't be used as a replacement for useRef.

Summary in points

  • useRef is a hook which stores the value (of a variable or a DOM element)
  • useRef doesn't cause the component to re-render hence updates on the ref aren't visible on the screen if there's no state update done
  • local variables cant be used in place of refs since they don't persist values on reloading

Concluding it, although storing values in refs isn't preferred, it can be used if there's no other way out.

I hope you understood the basics of useRef from this blog. Do let me know in the comments section if you have any particular doubts on this topic :)

brooklyn-nine-nine-amy-santiago.gif