useRef and DOM Refs in Depth
10 min read
Refs Beyond the Basics
useRef is more powerful than it first appears. Beyond simple DOM focus, you can use it to store previous values, track whether a component has mounted, and hold stable references to callbacks — all without triggering re-renders.
Tracking previous prop values
jsx
import { useRef, useEffect } from "react";
function PriceTracker({ price }) {
const prevPriceRef = useRef(price);
useEffect(() => {
prevPriceRef.current = price;
});
const prev = prevPriceRef.current;
const delta = price - prev;
return (
<p>
Price: {price}
{delta !== 0 && (
<span style={{ color: delta > 0 ? "green" : "red" }}>
{" "}({delta > 0 ? "+" : ""}{delta})
</span>
)}
</p>
);
}Forwarding refs to child components
By default, a ref placed on a custom component does not reach the DOM node inside it. Use React.forwardRef to pass a ref through the component boundary so the parent can access the inner DOM element directly.
jsx
import { forwardRef, useRef } from "react";
const FancyInput = forwardRef(function FancyInput(props, ref) {
return <input ref={ref} className="fancy-input" {...props} />;
});
function Form() {
const inputRef = useRef(null);
return (
<>
<FancyInput ref={inputRef} placeholder="Type here" />
<button onClick={() => inputRef.current.focus()}>Focus</button>
</>
);
}Use useImperativeHandle alongside forwardRef to expose only a narrow API from a child — e.g., { focus, reset } — rather than the raw DOM node. This keeps the parent from reaching into internal implementation details.
Ready to test yourself?
Practice React— quiz & coding exercises