When to Use useCallback and useMemo

These hooks are for optimization, not decoration. Here's when they actually help.

The Short Answer

**Most of the time, you don't need them.**

React is fast. Re-renders are usually fine. Premature optimization makes code harder to read.

What They Do

useCallback

Memoizes a function so it doesn't get recreated on every render:

// Without useCallback: new function every render
const handleClick = () => setCount(c => c + 1);

// With useCallback: same function reference
const handleClick = useCallback(() => {
  setCount(c => c + 1);
}, []); // dependencies

useMemo

Memoizes a computed value:

// Without useMemo: computed every render
const expensiveResult = calculateExpensiveThing(data);

// With useMemo: only recomputed when data changes
const expensiveResult = useMemo(() => {
  return calculateExpensiveThing(data);
}, [data]);

When They Help

useCallback: Passing to memoized children

const Parent = () => {
  const handleClick = useCallback(() => {
    doSomething();
  }, []);
  
  // Without useCallback, MemoizedChild re-renders every time
  return <MemoizedChild onClick={handleClick} />;
};

const MemoizedChild = React.memo(({ onClick }) => {
  return <button onClick={onClick}>Click</button>;
});

If the child isn't memoized, useCallback does nothing useful.

useMemo: Expensive calculations

const SearchResults = ({ items, query }) => {
  const filteredItems = useMemo(() => {
    return items.filter(item => {
      // Expensive filtering logic
      return complexMatch(item, query);
    });
  }, [items, query]);
  
  return <List items={filteredItems} />;
};

Only worth it if the calculation is actually expensive and the dependencies change less often than the component renders.

When They DON'T Help

Simple values

// ❌ Overkill
const name = useMemo(() => `${first} ${last}`, [first, last]);

// ✅ Just compute it
const name = `${first} ${last}`;

Functions not passed to children

// ❌ Pointless
const handleSubmit = useCallback(() => {
  api.submit(data);
}, [data]);

return <button onClick={handleSubmit}>Submit</button>;

Regular button doesn't care if the function reference changes.

Profile First

Before adding memoization:

1. Is there actually a performance problem? 2. Use React DevTools Profiler 3. Find the slow component 4. Then optimize specifically

Don't guess. Measure.

The Rule

  • Start without memoization
  • Notice a performance issue
  • Profile to identify the cause
  • Add memoization where it helps
  • Verify it helped

Readability matters more than micro-optimizations.

Back to Journal