Memoize expensive work

Let unrelated renders skip work with useMemo, useCallback and memo.

From frontend-patterns, “Performance Optimization: Memoization”.

500 cards, unrelated counter

Click +1, then Update one market. Rendered cards flash.

count = 0
memo, inline handlerBad

memo + useCallbackGood

onSelect={handleSelect}
const handleSelect = useCallback((id: string) => setSelectedId(id), [])

export const MarketCard = React.memo<MarketCardProps>(({ market, onSelect }) => (
  <button className="market-card" onClick={() => onSelect(market.id)}>
    <h3>{market.name}</h3>
    <p>{market.volume}</p>
  </button>
))

{markets.map((m) => <MarketCard key={m.id} market={m} onSelect={handleSelect} />)}

Sorting on every keystroke

Type in both boxes. Raise the slider to feel it.

Sort in renderBad
Last render
–
Sorts run
0
Top market
–
const sorted = [...markets].sort(byVolume)
useMemo on [markets]Good
Last render
–
Sorts run
0
Top market
–
useMemo(() => [...markets].sort(byVolume), [markets])
const [searchQuery, setSearchQuery] = useState('')

const sortedMarkets = useMemo(
  () => [...markets].sort((a, b) => b.volume - a.volume),
  [markets]
)

const handleSearch = useCallback((query: string) => setSearchQuery(query), [])

The in-place sort

Click Render child. Watch the parent list.

markets.sort(…)Bad

Parent, A to Z

    Child, by volume

      [...markets].sort(…)Good

      Parent, A to Z

        Child, by volume

          // sort() mutates and returns the same array
          const sortedMarkets = useMemo(
            () => [...markets].sort((a, b) => b.volume - a.volume),
            [markets]
          )

          Why it matters, pros and cons

          Why it matters

          A parent render reruns every child. Memo reuses results while inputs keep identity.

          Pros

          • Unrelated state stops paying for big lists.
          • One changed item re-renders one card.
          • Stable references keep dependency arrays quiet.

          Cons

          • Cheap work costs more memoized; measure first.
          • One inline arrow defeats React.memo.
          • React Compiler already does most of this.