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.
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.
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.
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.
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.