What on earth is useRef and do we really need it?

What is useRef? useRef is a React Hook that allows you to create a reference to a specific element or component in your code. It can be used to access the underlying DOM node or React element that the component represents. It is used to access the current value of the element, and also to change the value or properties of the element.

You do not always need to use useRef in React. It is not a necessary part of every component, and there are often other ways to achieve similar functionality without it. I’ll show a few use cases with and without using useRef.

Here is the first simple example of how you might use useRef to create a reference to a text input element:

// Example 1:

import { useRef } from 'react';

function App() {
  const inputRef = useRef(null);

  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={() => inputRef.current.focus()}>
        Focus the input
      </button>
    </>
  );
}

In this example, we create a reference to the input element by assigning it to the ref prop. We then use the current property of the ref object to access the underlying DOM node and invoke its focus() method when the button is pressed.

We can achieve the same without using useRef with just a little increase in verbosity:

// Example 1 without useRef:

import { useState } from 'react';

function MyComponent() {
  const [inputValue, setInputValue] = useState("");
  
  return (
    <>
      <input
        value={inputValue}
        onChange={e => setInputValue(e.target.value)}
        type="text" 
      />
      <button onClick={() => setInputValue("clicked")}>
        Focus the input
      </button>
    </>
  );
}


Another example of using useRef is when you want to store a value that should persist across renders, such as a timer ID.

// Example 2:

import { useRef } from 'react';

function MyComponent() {
  const intervalRef = useRef(null);

  useEffect(() => {
    intervalRef.current = setInterval(() => {
        console.log("interval running")
    }, 1000);
    return () => clearInterval(intervalRef.current);
  }, []);

  return (
    <>
      <button onClick={() => clearInterval(intervalRef.current)}>
        Stop the interval
      </button>
    </>
  );
}

Above, we are storing the interval ID in the intervalRef so that we can clear it later when the component unmounts or when the button is pressed.

And without useRef:

// Example 2 without useRef:

import { useState, useEffect } from 'react';

function MyComponent() {
  const [intervalId, setIntervalId] = useState(null);
  const [isRunning, setIsRunning] = useState(false);

  useEffect(() => {
    if (isRunning) {
        let id = setInterval(() => {
            console.log("interval running")
        }, 1000);
        setIntervalId(id);
    }
    return () => clearInterval(intervalId);
  }, [isRunning]);

  return (
    <>
      <button onClick={() => setIsRunning(!isRunning)}>
        Start/Stop the interval
      </button>
    </>
  );
}

In summary, useRef is used to store a value that should persist across renders, and it allows you to access the underlying DOM node or React element that the component represents. This can also be done using a combination of other hooks, but more often, using useRef in appropriate places will make the code look a lot simpler.