CPCODELAB
React

useMemo, useCallback, and React.memo — The Full Picture

13 min read

Memoisation Done Right

Memoisation in React has three interlocking tools: useMemo caches a computed value, useCallback caches a function reference, and React.memo prevents a component from re-rendering when its props are shallowly equal. They work together — each one alone is often insufficient.

The reference equality problem

Objects and functions are compared by reference, not by value. A new function or object created during render is a different reference every time, so passing it as a prop always breaks React.memo. This is why useCallback and useMemo exist.

jsx
import { memo, useMemo, useCallback, useState } from "react";

// memo: skip re-render if props didn't change
const FilteredList = memo(function FilteredList({ items, onSelect }) {
  console.log("FilteredList rendered");
  return (
    <ul>
      {items.map(item => (
        <li key={item.id} onClick={() => onSelect(item.id)}>{item.label}</li>
      ))}
    </ul>
  );
});

function App({ allItems }) {
  const [query, setQuery] = useState("");
  const [selected, setSelected] = useState(null);

  // useMemo: only re-filter when allItems or query changes
  const filteredItems = useMemo(
    () => allItems.filter(i => i.label.toLowerCase().includes(query)),
    [allItems, query]
  );

  // useCallback: stable reference for the memo-wrapped child
  const handleSelect = useCallback((id) => {
    setSelected(id);
  }, []);

  return (
    <>
      <input value={query} onChange={e => setQuery(e.target.value)} />
      <FilteredList items={filteredItems} onSelect={handleSelect} />
      <p>Selected: {selected}</p>
    </>
  );
}

When NOT to memoize

  • If the computation is fast (less than ~1 ms), useMemo overhead may exceed the savings.
  • If the component rarely re-renders, React.memo adds complexity for no gain.
  • If the dependency array changes on every render anyway, memoisation does nothing.

Profile first with the React DevTools Profiler. Memoisation is an optimization, not a default. Premature memoisation makes code harder to read and can even introduce bugs if dependencies are listed incorrectly.

Ready to test yourself?
Practice React— quiz & coding exercises