Mastering React Ref Callbacks: When and How to Use Them
This article explains the two meanings of React refs, how ref callbacks work during component mount and unmount, and presents four practical scenarios—scrolling, measuring, portal rendering, and sharing DOM elements—complete with code examples and best‑practice tips.
In React, "ref" has two related meanings that often cause confusion: the ref object returned by the useRef hook (a plain JavaScript object with a current property) and the ref attribute on JSX elements used to access the underlying DOM node.
ref callback
The ref attribute can also accept a function, known as a ref callback, whose sole argument is the DOM element. React invokes this function at specific lifecycle moments: immediately after the element is created (argument is the DOM node) and again when the element is removed (argument is null).
If the ref callback is defined inline, React calls it twice on each render—first with null, then with the DOM element—because a new function instance is created each render, ensuring consistency when props or state change.
To avoid unnecessary calls, wrap the callback in useCallback or define it outside the component.
Use Cases
Ref callbacks are useful only when you need to interact directly with a DOM element. The four common scenarios are:
Performing an operation on a DOM element when it mounts or updates (e.g., scrolling or focusing).
Notifying React of DOM changes by reading element properties (e.g., size or scroll position) and storing them in state.
Placing a DOM element into state so it can be accessed during render.
Sharing a DOM element among multiple consumers.
1. Scroll an element into view on mount
You can call DOM methods such as scrollIntoView inside a ref callback to automatically scroll to the last list item:
// On first render and on unmount there
// is no DOM element so `el` will be `null`
const scrollTo = (el) => {
if (el) {
el.scrollIntoView({ behavior: "smooth" });
}
};
function List({ data }) {
return (
<ul>
{data.map((d, i) => {
const isLast = i === data.length - 1;
return (
<li
key={d.name}
ref={isLast ? scrollTo : undefined}
>
{d.name}
</li>
);
})}
</ul>
);
}Remember that React should manage DOM mutations; only non‑destructive operations like focus or scroll are appropriate here.
2. Measure a DOM element
When you need to read element dimensions, a callback ref ensures the measurement occurs even if the element appears later (e.g., after a click). Example from the old React docs:
const [size, setSize] = useState();
const measureRef = useCallback((node) => {
setSize(node.getBoundingClientRect());
}, []);
return <div ref={measureRef}>{children}</div>;Using useCallback with an empty dependency array prevents the ref from changing on each render, avoiding unnecessary calls.
3. Access DOM in render (Portals)
Sometimes you need the DOM node during render, for example to create a portal. The ref callback can store the element in state and then pass it to ReactDOM.createPortal:
function Parent() {
const [modalElement, setModalElement] = useState(null);
return (
<div>
<div id="modal-location" ref={setModalElement} />
<Modal modalElement={modalElement}>Warning</Modal>
</div>
);
}
function Modal({ children, modalElement, ...props }) {
return modalElement
? ReactDOM.createPortal(
<ModalBase {...props}>{children}</ModalBase>,
modalElement
)
: null;
}This pattern is the basis of the “uncontrolled compound component” technique, where a ref callback, context, and portal work together to keep component distance short while preserving the DOM structure.
4. Share a DOM ref
When multiple consumers need the same element—e.g., measuring a <div> and passing it to a D3 or Observable Plot—you can capture the element with a callback ref and forward it:
import useMeasure from "react-use-measure";
import * as Plot from "@observablehq/plot";
export function BoxPlot({ data }) {
const [measureRef, { width, height }] = useMeasure({ debounce: 5 });
const plotRef = useRef(null);
useEffect(() => {
const boxPlot = Plot.plot({
width: Math.max(150, width),
marks: [Plot.boxX(data)],
});
plotRef.current.append(boxPlot);
return () => boxPlot.remove();
}, [data, width]);
const initBoxPlot = useCallback((el) => {
plotRef.current = el;
measureRef(el);
}, []);
return <div ref={initBoxPlot} />;
}Summary
Ref callbacks are functions passed to the ref attribute; they receive the DOM element on mount and null on unmount.
Changing the ref callback triggers React to call the previous callback with null and the new one with the element.
They enable specific DOM operations such as scrolling, measuring, portal rendering, and sharing the element with other libraries.
Uncontrolled compound components leverage ref callbacks, context, and portals to keep component trees clean while interacting with the DOM.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
