AtomLearnAtomLearn
DashboardGoalsPathAchievementsReviewSign In
DashboardReactuseMemo & useCallback
ReactNot Started

useMemo & useCallback

Memoize expensive calculations and stable function references

0%
Knowledge0%
Learn & Drill
Fluency0%
Drill & Speed
Retention0%
Mastery & Review
Confidence0%
All modes
Practice

Free tier: read the explanation here. Upgrade to Pro for Drills, Speed challenges & Mastery badges.

Upgrade
Knowledge
Fluency
Retention

Knowledge Debt detected

You can study this freely — but your score may plateau if these foundations have gaps. The Mastery badge requires them to be solid.

Explanation

These hooks help avoid unnecessary recomputations and re-renders via , but should only be used when there's a proven performance problem.

useMemo — memoize an expensive calculation result: ``jsx const sortedItems = useMemo( () => [...items].sort((a, b) => a.price - b.price), [items] // only re-sort when items changes );

Without useMemo, the sort runs on every render even if items hasn't changed.

useCallback — memoize a function reference: ``jsx const handleDelete = useCallback( (id) => setItems(prev => prev.filter(i => i.id !== id)), [setItems] // only new reference if setItems changes (it never does) );

Without useCallback, a new function object is created every render. This matters when passing callbacks to memoized child components (React.memo).

React.memo — skip re-render if props haven't changed: ``jsx const Item = React.memo(function Item({ name, onDelete }) { return <li onClick={() => onDelete(name)}>{name}</li>; }); // Only re-renders if name or onDelete changes reference

The rule: don't reach for these by default. Profile first. They add complexity and have their own overhead.

When to use:

  • useMemo: filtering/sorting large lists, expensive derivations
  • useCallback: callbacks passed to React.memo children, useEffect dependencies

Examples

useMemo for filtered list

Without useMemo, filtering runs on every render (e.g., unrelated state changes)

function ProductList({ products, searchTerm }) {
  const filtered = useMemo(
    () => products.filter(p =>
      p.name.toLowerCase().includes(searchTerm.toLowerCase())
    ),
    [products, searchTerm]
  );

  return <ul>{filtered.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}

useCallback to stabilize a handler

useCallback + React.memo prevents List from re-rendering when count changes

const List = React.memo(function List({ items, onRemove }) {
  console.log('List rendered');
  return items.map(i => (
    <div key={i.id}>
      {i.name} <button onClick={() => onRemove(i.id)}>×</button>
    </div>
  ));
});

function Parent() {
  const [items, setItems] = useState([...]);
  const [count, setCount] = useState(0);

  const handleRemove = useCallback(
    (id) => setItems(prev => prev.filter(i => i.id !== id)),
    []
  );
  // Without useCallback, handleRemove is a new function every render
  // causing List to re-render even when count changes

  return (
    <>
      <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
      <List items={items} onRemove={handleRemove} />
    </>
  );
}

How well did you understand this?