useRef Hook
In this page:
Accessing a DOM Element with useRef
Passing a ref object to an element's ref prop gives you direct access to the actual DOM node once it's rendered, available at ref.current. This is useful for things React doesn't have a built-in prop for, like focusing an input programmatically.
Note: ref.current for a DOM element is only populated AFTER the component renders -- access it inside an event handler or useEffect, not during render.
Warning: Reading ref.current directly in the render body (not inside an effect or handler) will be null on the first render, before the DOM exists.
Example: Accessing a DOM Element with useRef
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function FocusInput() {
const inputRef = React.useRef(null);
return (
<div>
<input ref={inputRef} placeholder="Click button to focus me" />
<button onClick={() => inputRef.current.focus()}>Focus Input</button>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<FocusInput />);
</script>
</body>
</html>
Storing a Mutable Value Without Re-rendering
Unlike state, updating a ref's .current value does not cause the component to re-render. This makes refs ideal for storing values you need to keep track of, like a timer ID or a previous value, without triggering unnecessary UI updates.
Note: Use a ref (not state) for values that the component needs to remember but that should never, by themselves, cause a re-render.
Warning: If you need the UI to visually update when a value changes, use state instead -- a ref update alone won't do that.
Example: Storing a Mutable Value Without Re-rendering
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function ClickCounter() {
const clicksRef = React.useRef(0);
function handleClick() {
clicksRef.current += 1;
console.log("Clicked", clicksRef.current, "times (check console, no re-render)");
}
return <button onClick={handleClick}>Click me (see console)</button>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<ClickCounter />);
</script>
</body>
</html>
useRef vs useState
Both persist a value across renders, but they serve very different purposes: state changes should be visible in the UI and trigger a re-render, while ref changes are typically 'behind the scenes' bookkeeping that shouldn't affect what's displayed. Choosing the right one avoids both unnecessary re-renders and missing UI updates.
Note: Ask: 'should the screen update when this value changes?' If yes, use state. If no, use a ref.
Warning: Using a ref for a value that should be visible on screen means the UI silently won't update when it changes -- a common source of confusing bugs.
Example: useRef vs useState
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function Comparison() {
const [stateValue, setStateValue] = React.useState(0);
const refValue = React.useRef(0);
return (
<div>
<p>State (updates UI): {stateValue}</p>
<button onClick={() => setStateValue(stateValue + 1)}>Update State</button>
<button onClick={() => { refValue.current += 1; console.log("Ref is now", refValue.current); }}>Update Ref (check console)</button>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<Comparison />);
</script>
</body>
</html>
- Expecting a change to a ref's
.currentvalue to trigger a re-render -- it never does. - Reading
ref.currentduring the render itself for a DOM element ref, before the DOM has actually been created. - Confusing useRef with useState -- refs are for values that don't need to cause re-renders, state is for values that do.
- useRef returns a mutable object with a single
.currentproperty. - Changing
.currentdoes NOT trigger a re-render, unlike state. - A common use is accessing a DOM element directly, via the
refprop. - Another common use is storing a mutable value that persists across renders without causing updates.
Available since React 16.8, when Hooks were introduced.
Chapter Quiz — Complete all 12 topics to unlock
0/12 topics done
Complete these topics first: