Why does useCallback often do nothing unless the child is wrapped in React.memo?
useCallback keeps a function reference stable, but a child still re-renders whenever its parent does unless the child is wrapped in React.memo or the function is a hook dependency. The stable reference only helps where something compares it.
Explain it simply
Giving every guest the same ID card number does nothing if the gatekeeper never checks the number. useCallback gives the function the same identity across renders, but identity only matters if something compares it.
A normal child component re-renders whenever its parent renders, regardless of props. React.memo adds the check: skip if all props are equal by Object.is. Only then does a stable callback prevent a re-render.
The other place it matters is dependency arrays. If an effect lists a function, a new function each render re-runs the effect. A stable function from useCallback fixes that.
A good rule: reach for useCallback only after you can name the exact comparison that benefits, either a React.memo child or a dependency array. If you cannot name it, the hook is decoration.
Example
const Row = React.memo(function Row({ item, onSelect }) {
return <li onClick={() => onSelect(item.id)}>{item.name}</li>;
});
function Table({ items }) {
const [picked, setPicked] = useState(null);
const onSelect = useCallback(id => setPicked(id), []);
return <ul>{items.map(i => <Row key={i.id} item={i} onSelect={onSelect} />)}</ul>;
}What to say in the interview
"useCallback only preserves a function's identity. It pays off when the receiving component is memoised with React.memo or when the function sits in a dependency array. Without such a comparison, the child re-renders anyway and I have only added overhead."
Common mistakes
- Wrapping every handler in useCallback by habit.