How to use the useRef React hook
By Flavio Copes
Learn how the useRef React hook lets you access a DOM element imperatively by attaching a ref and reading it through the current property of the ref.
Check out my React hooks introduction first, if you’re new to them.
The useRef React hook gives you a way to access a DOM element imperatively, from your component code.
Import it from React:
import React, { useRef } from 'react'
Calling useRef(null) returns a ref object. It has a single property, current, initialized to the value you pass in.
React keeps this object stable across renders. Your component can re-render a hundred times, and the ref is always the same object.
Attaching the ref to a DOM element
To link the ref to an element, pass it to the ref attribute in JSX. Once React renders the element, current points to the actual DOM node.
Here’s an example, where I log to the console the value of the DOM reference of the span element that contains the count value:
import React, { useState, useRef } from 'react'
const Counter = () => {
const [count, setCount] = useState(0)
const counterEl = useRef(null)
const increment = () => {
setCount(count + 1)
console.log(counterEl)
}
return (
<>
Count: <span ref={counterEl}>{count}</span>
<button onClick={increment}>+</button>
</>
)
}
ReactDOM.render(<Counter />, document.getElementById('app'))
Notice the const counterEl = useRef(null) line, and the <span ref={counterEl}>{count}</span>. This is what sets the link.
Now we can access the DOM reference by accessing counterEl.current.
From there you can do anything the DOM allows: call counterEl.current.focus() on an input, read counterEl.current.offsetHeight, and so on.
See it on Codepen: https://codepen.io/flaviocopes/pen/orENKo/
Storing values that survive renders
DOM access is not the only use. A ref can hold any value, and that value survives re-renders.
This is useful for things like timer IDs:
const timerId = useRef(null)
const start = () => {
timerId.current = setInterval(tick, 1000)
}
const stop = () => {
clearInterval(timerId.current)
}
A plain variable inside the component would be reset on every render. The ref keeps its value.
Be careful: changing a ref does not re-render
This is the pitfall people hit most often. Updating current does not trigger a re-render.
If you store a counter in a ref and display it in JSX, the number on screen won’t update when you change it. Nothing tells React to render again.
The fix: if the value should show up in the UI, use useState. Use a ref only for values React doesn’t need to react to.
Also, remember the ref is null until React renders the element. Don’t read current during the first render. Read it in event handlers or in useEffect, which run after the DOM exists.