Skip to content

useCallback Hook

useCallback returns a memoized version of a function that only changes when dependencies change. It helps prevent child re-renders when passing callbacks to React.memo components.

Stable Function References

const fn = useCallback(() => doWork(a), [a]) is equivalent to useMemo(() => () => doWork(a), [a]). The cached function reference stays the same across renders if deps are unchanged.

Inline arrow functions in JSX create new references every render, which can break memoization of child components comparing props by reference.

const handleClick = useCallback(() => {
  onSelect(item.id);
}, [item.id, onSelect]);

return <MemoizedRow onClick={handleClick} />;

MemoizedRow skips re-render if onClick reference is unchanged.

useCallback Pattern

const memoizedFn = useCallback(
  (arg) => { /* uses dep1, dep2 */ },
  [dep1, dep2]
);
  • Include all values from scope that the callback uses.
  • Pair with React.memo on children to see benefit.
  • Without memoized children, useCallback rarely helps.
  • Event handlers in non-memoized children don't need useCallback.

useCallback Decision Guide

When useCallback is worth the complexity.

Scenario Use useCallback?
Callback to React.memo child Often yes
Callback in dependency array of effect Often yes
Custom hook returning stable API Sometimes
Handler on plain DOM element Usually no
List of 1000 memoized items Profile first

Stable Callbacks in Context Providers

Context values that include inline functions cause all consumers to re-render. useCallback and useMemo stabilize context value objects.

const value = useMemo(
  () => ({ user, logout: handleLogout }),
  [user, handleLogout]
);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;

React Compiler and Manual Memoization

React 19's experimental React Compiler can auto-memoize in some setups, reducing manual useCallback/useMemo. Until then, manual memoization remains common in performance-critical paths.

Common Mistakes

  • Wrapping every handler in useCallback without memoized children.
  • Stale closures from incomplete dependency arrays.
  • Using useCallback instead of fixing architecture problems.
  • Assuming useCallback speeds up the parent component.

Key Takeaways

  • useCallback memoizes function references.
  • Most useful with React.memo children and effect deps.
  • Not needed for most everyday event handlers.
  • Profile and measure before adding widely.

Pro Tip

React Docs now say: skip useCallback until profiling shows a child re-render problem. Good default advice.