useRef
is a React Hook that allows you to create a mutable reference object. This reference object can hold a value that persists across renders and changing its .current
value does NOT trigger a re-render. It's also commonly used to get direct access to DOM elements.
What is useRef?
useRef
is a React hook returning a mutable ref
object with a single .current
property.
const myRef = useRef(initialValue);
// myRef looks like: { current: initialValue }
Initialization: The
initialValue
you provide is used to setmyRef.current
only during the component's initial render. It's ignored on subsequent renders. If you omit the argument,initialValue
defaults toundefined
.Return Value:
useRef
returns the same ref object on every render of a given component instance. This is how it persists across renders.
🗝️Key Characteristics:
Mutable
.current
Property:
You directly modify theref.current
property (e.g.,myRef.current = newValue
), unlike state variables fromuseState
(which you update via a setter function),No Re-render on Change:
Assigning a new value toref.current
does not trigger a component re-render. This is the most significant difference fromuseState
.Persistence Across Renders:
The ref object itself (and the value stored in ref.current) persists for the full lifetime of the component. React ensures you get the same ref object back on every render.
⚠️When Should You Mutate ref.current
?
Since mutating ref.current
doesn't cause a re-render, it's important when you perform the mutation.
✅Safe: Inside event handlers (like
onClick
,onChange
) or insideuseEffect
hooks. These run after the render cycle is complete or as a result of user interaction, making mutations predictable.❌Avoid (Generally): Mutating
ref.current
directly during the rendering phase (i.e., right inside the main body of your functional component). While React won't stop you, reading a ref during render is fine, but writing to it can make component behavior less predictable. This is because the component body should ideally be free of side effects, and mutations might not behave consistently depending on React's rendering optimizations (like Concurrent Mode). If you need to calculate something based on render and store it mutably,useEffect
is usually the better place.
🔧Common Use Cases
useRef
serves two primary purposes in React components:
1. Accessing DOM Nodes
This is a very common use case. useRef
allows you to get a direct reference to a specific DOM element rendered by your component.
-
How it works:
- Create a ref, usually initialized to null:
const myInputRef = useRef(null);
- Attach this ref to a JSX element using the special ref prop:
<input ref={myInputRef} />
. - After the component mounts and React creates the DOM node for the
<input>
, React will setmyInputRef.current
to that DOM node instance.
- Create a ref, usually initialized to null:
-
Why it's needed:
For interacting directly with the DOM, which is sometimes necessary for:- Managing focus (e.g.-
myInputRef.current.focus()
). - Measuring element dimensions or position.
- Triggering imperative animations.
- Integrating with third-party libraries that need direct DOM access.
- Managing focus (e.g.-
import React, { useRef, useEffect } from 'react';
function TextInputWithFocusButton() {
// 1. Create a ref to hold the input DOM element
const inputEl = useRef(null);
// Example: Focus on initial mount
useEffect(() => {
inputEl.current?.focus();
}, []);
// Empty dependency array means run once after initial mount
return (
<>
{/* 2. Attach the ref to the input element */}
<input ref={inputEl} type="text" />
</>
);
}
2. Storing Mutable Values (Like Instance Variables)
You can use useRef
to keep track of any mutable value that needs to persist across renders but should not cause a re-render when it changes.
- Storing previous state or prop values.
- Managing timer IDs (e.g., from
setTimeout
orsetInterval
). Keeping track of internal component state that doesn't directly affect the rendered output.
-
How it works:
-
Initialization:
You call
useRef(initialValue)
during your component's setup. React creates a plain JavaScript object like{ current: initialValue }
. -
Persistence:
React internally holds onto this specific object for the entire lifetime of your component instance. On every subsequent render, the exact same object is returned by the
useRef
hook. -
Accessing:
You can read the stored value anytime using
yourRef.current
. -
Updating:
You can change the stored value by directly assigning a new value to the
.current property
(e.g.,yourRef.current = newValue
).
-
Initialization:
You call
⚠️No Re-render: this assignment to
.current
is just a standard JavaScript object property mutation. React does not monitor this change, and therefore, it does not trigger a component re-render. The value is updated silently within the persistent ref object.
The Analogy: The Room, The Closet, and The Label Maker
Imagine your React component is like a room. Every time the component "re-renders" (because its state or props change), it's like the room gets quickly dismantled and rebuilt with the updated information.
useState
: Think ofuseState
as the visible furniture or decorations in the room (like a couch or a painting). When you change something using theset
function (e.g., changing the couch color), React notices this visible change and rebuilds the room (re-renders the component) to show the update. Everyone sees the new couch color.useRef
: Now,useRef
gives you access to persistent tools that don't cause a room rebuild when used or changed. It primarily provides two kinds of tools:
The Hidden Storage Closet (for Mutable Values):
- Calling
useRef(initialValue)
can create a small, hidden storage closet inside the room, with an initial item placed on its single shelf, labelled.current
. - This closet (
ref
object) and its contents persist across room rebuilds (re-renders). Even when the room is dismantled and rebuilt, the closet remains exactly where it is, untouched. - You can open the closet anytime (preferably in effects or handlers) and change the item on the
.current
shelf (myRef.current = newValue
). - Crucially: Changing the item inside the hidden closet does not cause the room to be rebuilt (it does not trigger a re-render). It's a silent change, hidden from the main rendering process.
The Persistent Label Maker (for DOM Access):
- Calling
useRef(null)
can also give you a persistent label maker. - When you build the room, you can use the special
ref
prop on a specific part of the room's structure (like a window, a door, or a light switch – representing a DOM element like<input ref={myRef} />
). This tells React: "Use themyRef
label maker to put a unique, persistent label on this specific window when you build it." - After the room is built (the component mounts and renders the element), the label maker's display (
myRef.current
) now points directly to that specific, labeled window (the actual DOM node). - You can then use this reference (
myRef.current
) to interact directly with that specific part of the room (e.g.,myRef.current.focus()
to look through the window, ormyRef.current.style.display = 'none'
to cover it) without needing to rebuild the entire room (no re-render is triggered by accessing or manipulating the element via the ref).
In essence, useRef
provides ways to either hold onto information silently (closet
) or get a direct, persistent handle (label maker
) to parts of the rendered output, all without interfering with React's normal state-driven rendering cycle.
🔄 useState
vs useRef
– Quick Comparison
Feature | useState |
useRef |
---|---|---|
Triggers re-render on change? | ✅ Yes | ❌ No |
Persists across renders? | ✅ Yes | ✅ Yes |
Used for DOM access? | ❌ No | ✅ Yes |
Ideal for | UI-related state that affects rendering | Mutable values or DOM refs that don’t affect UI |
How to update value | Using setter function (e.g., setValue() ) |
Direct mutation (myRef.current = newValue ) |
Can be used in render phase? | ✅ Yes (safe) | ⚠️ Reading: Yes / Writing: Avoid in render phase |
Wrapping Up
useRef
is a powerful tool in your React toolkit, essential for interacting with the DOM and managing persistent mutable values that shouldn't trigger re-renders. Understanding when and why to use it over useState
is key to writing efficient and effective React components.
when and why to use useRef
:
- You need direct access to a DOM element for imperative actions like focusing, measuring, or integrating with external libraries.
useState
cannot do this. - You need to store mutable data that persists across renders, but changing this data should NOT cause a re-render. This is useful for performance or specific logic needs.
- Think: Timer IDs (setTimeout/setInterval), tracking previous state/props, flags not directly rendered.
In short: useState
is for state that drives your UI updates. useRef
is for references to DOM nodes or for holding mutable data silently behind the scenes.
Likes, Comments and feedback are welcome!😅
Happy coding!
Top comments (1)
It helped me, thank you!