Understanding the useRef hook

Jan 31

·
views
·
likes

The useRef hook is a hook I have found hard to understand, owing to the fact I found it similar to the useState hook. Before we look at the differences and similarities between the two hooks we'll first have to understand the useRef hook, what is used for(with examples), when to use it and when not to use it(because knowing when not to use something is just as important as knowing when to use it).

The useRef hook is a core react hook that enables you to persist a mutable value between renders that is not needed for rendering. The mutable value stored does not trigger a rerender when mutated. The useref hook has two uses:

  • Referencing a value with a ref.
  • Accessing the DOM with a ref.

The useRef hook takes an initialValue of any type and returns a ref object. The ref object has a current property that can be used to reference the value. The current property is used to reference a mutable value or to access browser APIs.

const ref = useRef(initialValue)
// returns {current: initialValue}

useRef Background

The useRef hook is used to create refs in react. A ref is a special object that can be used to reference a value in the DOM or a value in a component. In react a ref is used as an escape hatch to access the DOM and browser APIs that react does not expose.

React is a declarative library, meaning that you describe what you want to happen and react takes care of the details. This means that you don't have to worry about how the DOM is updated, react takes care of that for you. This is why you don't have to worry about how the DOM is updated when you use the useState hook to update the state of a component. When you need to access the DOM directly, this requires an imperative approach, which means you describe how you want something to happen hence you will need to "step outside" react. A ref is used to "step outside" react hence it is considered an escape hatch.


Refencing a value with a ref

Refs can be used to remember a value, mutate the value and persist it across the component's lifecycle. The useRef hook is used to create a ref. The useRef hook takes an initial value as an argument and returns a ref object.

In the code shown below the ref created with useRef has an initialValue of null. It then alternates between referencing primaryImg and secondaryImg both of type string when the mouse hovers over the image. This enables you to switch between two images when the mouse hovers over the image and switch back when the mouse leaves the image.

PhotoRef.tsx
export default function PhotoRef({
  primaryImg,
  secondaryImg,
}: {
  primaryImg: string
  secondaryImg: string
}) {
  const imageRef = useRef<any>(null)

  return (
    <img
      onMouseOver={() => {
        imageRef.current.src = secondaryImg
      }}
      onMouseOut={() => {
        imageRef.current.src = primaryImg
      }}
      src={primaryImg}
      alt="A photo of Westlands,Nairobi by Antony Trivet."
      ref={imageRef}
    />
  )
}

If you hover your mouse over the image below you will see the image change to a black and white version of the image. Then it will switch back to the original image when you move your mouse away from the image.

A photo of Westlands,Nairobi by Antony Trivet.
A photo of Westlands, Nairobi by Antony Trivet.

As you can see the changes occurred without having to rerender the component. This is because the changes made by the ref do not trigger a re-render.


Manipulating the DOM

The useRef hook as stated earlier, can be used to access the DOM elements. In most cases, react manages and automatically updates the DOM for you. However, there are times you need to access the DOM directly, for example, media playback, to get access to browser APIs such as mouse position, to focus on a node, etc. In the examples that follow we will see how useRef can be used to access and manipulate the DOM.

VideoRef.tsx
export default function VideoRef() {
  const [isPlaying, setIsPlaying] = useState(false)
  const videoRef = useRef<any>(null)
  function handleClick() {
    const nextIsPlaying = !isPlaying
    setIsPlaying(nextIsPlaying)
    if (nextIsPlaying) {
      videoRef.current.play()
    } else {
      videoRef.current.pause()
    }
  }
  return (
    <div className="flex flex-col justify-center">
      <video
        width="300"
        ref={videoRef}
        onPlay={() => setIsPlaying(true)}
        onPause={() => setIsPlaying(false)}
      >
        <source
          src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
          type="video/mp4"
        />
      </video>
      <button onClick={handleClick} className="flex justify-center">
       {isPlaying ? (
          <svg
            xmlns="http://www.w3.org/2000/svg"
            fill="none"
            viewBox="0 0 24 24"
            strokeWidth={1.5}
            stroke="currentColor"
            className="h-6 w-6"
          >
            <path
              strokeLinecap="round"
              strokeLinejoin="round"
              d="M15.75 5.25v13.5m-7.5-13.5v13.5"
            />
          </svg>
        ) : (
          <svg
            xmlns="http://www.w3.org/2000/svg"
            fill="none"
            viewBox="0 0 24 24"
            strokeWidth={1.5}
            stroke="currentColor"
            className="h-6 w-6"
          >
            <path
              strokeLinecap="round"
              strokeLinejoin="round"
              d="M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.348a1.125 1.125 0 010 1.971l-11.54 6.347a1.125 1.125 0 01-1.667-.985V5.653z"
            />
          </svg>
        )}
      </button>
    </div>
  )
}

From the code above you can see that the ref is created with useRef and passed to the video element. The ref is then used to access the video element and call the play() and pause() methods to control the video playback. These methods are not made available by react and hence you need to access the DOM directly.

Clicking the icon below will play the video. Clicking it again will pause the video.

An example of how to play and pause a video using useRef

When not to use useRef

The useRef hook should not be used in the following ways:

  • To store a value used for rendering. This is because the value will not be updated when the component rerenders. Instead, use useState to store a value used for rendering.
  • It should not read or write during rendering except for initialization. It makes your component behavior unpredictable.

useRef vs useState

Both useRef and useState are that persist a mutable value in a component across rerenders. This means that the value is not reset to the initial value when the component rerenders but remains at the current value.

The difference between the two hooks is that the value referenced by useState is updated via calling the setter function, which triggers a re-render while the value referenced by useRef is updated via direct mutation


useRef vs createRef

They are both used to create a ref but useRef is a hook while createRef is a class. The createRef class is used to create a ref that is attached to a class component. The useRef hook is used to create a ref that is attached to a functional component.


Closing thoughts

In this article we have seen how to use the useRef hook to create a ref, reference a value with a ref and manipulate the DOM. We have also seen how the useRef hook is different from the useState hook and the createRef class.