React provides powerful tools for managing DOM references and persisting mutable values with useRef
and ref callbacks. These tools help ensure optimal performance, clean integration with third-party libraries, and effective state management. In this article, we’ll explore useRef
and ref callbacks, understand their differences, and learn how to use ref callbacks efficiently by wrapping them with useCallback
.
What is useRef
?
useRef
creates a mutable reference object that persists across renders and stores a .current
property. This is often used for DOM manipulation or storing values that don’t trigger re-renders.
Key Features of useRef
:
-
Persistent Storage: Maintains its
.current
property across renders. -
No Re-renders: Changes to
.current
don’t cause a component to re-render. - DOM Manipulation: Commonly used to reference DOM elements.
Example: Using useRef
for DOM Manipulation
import React, { useRef } from 'react';
const UseRefExample = () => {
const inputRef = useRef<HTMLInputElement | null>(null);
const focusInput = () => {
if (inputRef.current) {
inputRef.current.focus();
}
};
return (
<div>
<input ref={inputRef} placeholder="Focus me" />
<button onClick={focusInput}>Focus Input</button>
</div>
);
};
export default UseRefExample;
Explanation:
- The
useRef
hook initializes a reference tonull
. - The reference is assigned to the input element when it mounts, making it accessible through
inputRef.current
.
Ref Callbacks: A Dynamic Alternative
Ref callbacks are functions React calls to dynamically assign or clean up references. Unlike useRef
, ref callbacks allow more flexibility in managing dynamic elements.
Key Features of Ref Callbacks:
- Dynamic Assignment: Handles DOM element references dynamically on mount/unmount.
-
Automatic Cleanup: React calls the ref callback with
null
on unmount, ensuring proper cleanup. -
No Persistent State: Unlike
useRef
, they don’t store values persistently unless explicitly managed.
Example: Ref Callbacks for Dynamic DOM Manipulation
import React, { useCallback } from 'react';
const RefCallbackExample = () => {
const setRef = useCallback((node: HTMLDivElement | null) => {
if (node) {
node.style.backgroundColor = 'lightblue';
console.log('Node assigned:', node);
} else {
console.log('Node unmounted');
}
}, []);
return <div ref={setRef}>Hello, Ref Callback!</div>;
};
export default RefCallbackExample;
Explanation:
- React calls
setRef
with the DOM element when the component mounts and withnull
when it unmounts. - Using
useCallback
ensures that the callback remains stable across renders.
Comparing useRef
and Ref Callbacks
Feature | useRef |
Ref Callback |
---|---|---|
Persistence | Stores a stable .current property |
Assigns dynamically on mount/unmount |
Reactivity | Static and consistent | Reactively updated by React |
Automatic Cleanup | Manual cleanup required | React calls with null on unmount |
Optimizing Ref Callbacks with useCallback
By default, ref callbacks are re-created on every render. This can lead to unnecessary reassignments and degrade performance. Wrapping ref callbacks with useCallback
ensures they remain stable.
Example: Ref Callback with Dynamic Elements
import React, { useCallback, useState } from 'react';
const DynamicRefExample = () => {
const [showInput, setShowInput] = useState(true);
const setInputRef = useCallback((node: HTMLInputElement | null) => {
if (node) {
console.log('Input mounted:', node);
node.focus();
} else {
console.log('Input unmounted');
}
}, []);
return (
<div>
<button onClick={() => setShowInput((prev) => !prev)}>
Toggle Input
</button>
{showInput && <input ref={setInputRef} placeholder="Dynamic Ref" />}
</div>
);
};
export default DynamicRefExample;
Use Cases for Ref Callbacks
1. Dynamic DOM Elements
Ref callbacks are ideal when elements are dynamically added or removed.
function DynamicChart({ show }: { show: boolean }) {
const setChartRef = useCallback((node: HTMLCanvasElement | null) => {
if (node) {
console.log('Chart canvas initialized');
}
}, []);
return show ? <canvas ref={setChartRef}></canvas> : null;
}
2. Third-Party Libraries
Ref callbacks simplify integration with libraries like D3.js or Chart.js.
import { Chart } from 'chart.js';
const ChartExample = () => {
const setCanvasRef = useCallback((canvas: HTMLCanvasElement | null) => {
if (canvas) {
const chart = new Chart(canvas, {
type: 'bar',
data: { labels: ['A', 'B'], datasets: [{ data: [10, 20] }] },
});
return () => chart.destroy();
}
}, []);
return <canvas ref={setCanvasRef}></canvas>;
};
Final Thoughts: Choosing Between useRef
and Ref Callbacks
- Use
useRef
when:- A stable, persistent reference is required (e.g., form inputs).
- No need for dynamic reassignment.
- Use ref callbacks when:
- DOM elements are dynamically added or removed.
- Cleanup logic is crucial.
- Third-party libraries require DOM node references.
By understanding the nuances between useRef
and ref callbacks, you can choose the right tool for the job and build performant, clean React applications. 🚀
Top comments (0)