useCallback

A Function That Keeps Its Identity Across Renders

useCallback

`useCallback(fn, deps)` is `useMemo` for functions — same fn reference as long as deps don't change.

3 min read Level 2/5 #react#useCallback#hooks
What you'll learn
  • Stabilize a callback's identity
  • Recognize when `useCallback` matters

Defining a function inside a component creates a brand-new function every render. Usually that’s fine. Sometimes it matters — when that function is in another hook’s deps, or passed as a prop to a memo-wrapped child.

useCallback(fn, deps) keeps the same reference until deps change.

The Shape

const onSave = useCallback(() => {
  send(form);
}, [form]);

It’s literally useMemo(() => fn, deps). Same semantics, just a shortcut for functions.

When It Actually Helps

Stabilizing a dep

function Search({ id }) {
  const fetchUser = useCallback(() => {
    return fetch(`/api/users/${id}`).then(r => r.json());
  }, [id]);

  useEffect(() => {
    fetchUser().then(setData);
  }, [fetchUser]);   // re-runs only when id changes
}

Without useCallback, fetchUser is new every render, the effect sees a “new” dep every render, and it runs in a loop.

Passing to a memo’d child

const SaveButton = memo(function SaveButton({ onClick }) {
  return <button onClick={onClick}>Save</button>;
});

function Form() {
  const onSave = useCallback(() => { ... }, []);
  return <SaveButton onClick={onSave} />;
}

Without useCallback, every render passes a new onClick, so the memo wrapper can’t skip — the child re-renders too.

When It’s Pointless

Wrapping every function in useCallback is a common over-correction. If the function is only passed to a plain DOM <button>, identity doesn’t matter. The button doesn’t re-render based on whether onClick is a new function — it just calls whatever you give it.

Function passed to…useCallback worth it?
A plain <div onClick={fn}>No
Another hook’s depsYes
A memo-wrapped childYes
Just used inside the componentNo

Same Deps Rules

Everything reactive that the function reads should be in deps. ESLint enforces this.

Up Next

Sharing data across components without prop drilling.

useContext →