useRef Hook — Working with DOM and Storing Values Without Re-renders
The useRef hook in React has two main roles:
- Allows accessing DOM elements (e.g., focusing an input or measuring dimensions).
- Stores mutable values that don't cause re-renders unlike state.
useRef returns an object in this format:
{ current: ... }React stores the reference in this current property.
🔹 Syntax
const ref = useRef(initialValue);
- initialValue — initial value to be stored in the ref.
- ref.current — where the data or DOM reference is stored.
📍 Example 1 — Focus Input via DOM
This is the most famous and common example: accessing input via ref.
import React, { useRef } from 'react';
export default function InputFocus() {
const inputRef = useRef(null);
function handleFocus() {
inputRef.current.focus();
}
return (
<div>
<input ref={inputRef} placeholder="Write something..." />
<button onClick={handleFocus}>Focus</button>
</div>
);
}
In this example, `inputRef.current` references the input DOM element. By clicking the button, we programmatically focus the input.
📍 Example 2 — ref as "Persistent" Value
If you want to store a variable that persists across re-renders but doesn't cause re-renders, use ref. This is very useful for storing timers, previous values, or metrics.
import React, { useRef, useState, useEffect } from 'react';
export default function RenderCounter() {
const [count, setCount] = useState(0);
const renders = useRef(0);
useEffect(() => {
renders.current = renders.current + 1;
});
return (
<div>
<p>Count: {count}</p>
<p>Render count: {renders.current}</p>
<button onClick={() => setCount(prev => prev + 1)}>Increment</button>
</div>
);
}
In this example, the `renders.current` value preserves the render count, but doesn't cause re-renders even when changed. If we did the same with state, it would cause infinite re-renders.
🧠 Why ref Doesn't Cause Re-renders
React only re-renders when state or props change. Ref is specifically designed to be a "persistent storage" without triggering re-renders. React simply remembers that value throughout the component's entire lifecycle.
📍 Example 3 — Storing Previous State Value
This is a professional example: when you want to know what the previous state value was.
import React, { useState, useEffect, useRef } from 'react';
export default function PreviousValue() {
const [name, setName] = useState('');
const prevName = useRef('');
useEffect(() => {
prevName.current = name; // save previous value
}, [name]);
return (
<div>
<input value={name} onChange={e => setName(e.target.value)} />
<p>Current name: {name}</p>
<p>Previous name: {prevName.current}</p>
</div>
);
}
Every time name changes, we save the previous value in the ref. This is frequently used in form validation, comparisons, or animations.
⚙️ Example 4 — Storing Interval via Ref
If setInterval is created in an effect, ref can help preserve the latest callback without creating a new interval on every render.
import React, { useState, useEffect, useRef } from 'react';
export default function Timer() {
const [seconds, setSeconds] = useState(0);
const intervalRef = useRef(null);
useEffect(() => {
intervalRef.current = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
return () => clearInterval(intervalRef.current);
}, []);
return (
<div>
<p>Elapsed time: {seconds} seconds</p>
<button onClick={() => clearInterval(intervalRef.current)}>Stop</button>
</div>
);
}
The ref here stores the interval ID so it's easy to stop it without changing state.
⚡ useRef vs useState
| useState | useRef |
|---|---|
| Changes cause re-renders. | Changes don't cause re-renders. |
| Stores data for rendering. | Stores data throughout component lifecycle. |
| Used for UI updates. | Used for DOM or internal data. |
🎯 Exercise (Try it yourself)
Write a component that:
- Has an input field and a button that focuses the input.
- Displays how many times the component has re-rendered.
- Preserves the previous input value.
Use only one hook: useRef for all three purposes.
📘 Summary
- useRef — React hook that stores references to DOM or data.
- Changing ref doesn't cause re-renders.
- You can use it for DOM manipulations, timers, and previous values.
- Ref in React remembers its value throughout the component's entire lifecycle.