ReactuseMemo: Memoizing Values

useMemo: Memoizing Values

useMemo is a performance optimization hook that caches the result of a computation between renders. Instead of re-running an expensive calculation on every render, React returns the cached value and only recomputes when the specified dependencies change.

The Signature

JSX
const memoizedValue = useMemo(
  () => expensiveComputation(a, b), // factory function
  [a, b]                            // dependency array
)
  • On the first render, the factory function runs and its result is cached

  • On subsequent renders, if a and b are the same, React returns the cached value without calling the factory

  • When a or b changes, the factory runs again and the new result is cached

When Every Render Is Expensive: Filtering a Large List

Without useMemo, this filter runs on every re-render — even when the parent component re-renders for an unrelated reason (like a theme toggle).

JSX
import { useState, useMemo } from 'react'

// Simulated large dataset
function generateItems(count) {
  return Array.from({ length: count }, (_, i) => ({
    id: i,
    name: `Item ${i}`,
    price: Math.random() * 100,
    inStock: Math.random() > 0.3,
  }))
}

const allItems = generateItems(10_000)

// ❌ Without useMemo — filter runs on every render
function ProductList({ minPrice }) {
  const [theme, setTheme] = useState('light')

  // This runs every time 'theme' changes, even though minPrice didn't change
  const filtered = allItems.filter(item => item.price >= minPrice && item.inStock)

  return (
    <div className={theme}>
      <button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
        Toggle theme
      </button>
      <ul>{filtered.map(item => <li key={item.id}>{item.name} — ${item.price.toFixed(2)}</li>)}</ul>
    </div>
  )
}

// ✅ With useMemo — filter only runs when minPrice changes
function ProductList({ minPrice }) {
  const [theme, setTheme] = useState('light')

  const filtered = useMemo(
    () => allItems.filter(item => item.price >= minPrice && item.inStock),
    [minPrice] // only recompute when minPrice changes
  )

  return (
    <div className={theme}>
      <button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
        Toggle theme
      </button>
      <ul>{filtered.map(item => <li key={item.id}>{item.name} — ${item.price.toFixed(2)}</li>)}</ul>
    </div>
  )
}

Toggling the theme re-renders ProductList, but filtered is returned from the cache — the expensive filter over 10,000 items does not run.

Referential Stability for React.memo Children

useMemo is not just about expensive calculations. It is equally important for referential stability — ensuring objects and arrays maintain the same reference between renders so that memoized children do not re-render unnecessarily.

JSX
import { useState, useMemo, memo } from 'react'

// A chart that is expensive to render
const Chart = memo(function Chart({ data }) {
  console.log('Chart re-rendered')
  return <svg>{/* expensive rendering */}</svg>
})

// ❌ Without useMemo — Chart re-renders on every keystroke
function Dashboard({ userId }) {
  const [filter, setFilter] = useState('')

  // New object reference on every render → Chart sees "new" data → re-renders
  const chartData = {
    userId,
    series: computeSeriesData(userId), // expensive
  }

  return (
    <div>
      <input value={filter} onChange={e => setFilter(e.target.value)} />
      <Chart data={chartData} />
    </div>
  )
}

// ✅ With useMemo — Chart only re-renders when userId changes
function Dashboard({ userId }) {
  const [filter, setFilter] = useState('')

  // Stable reference — same object as long as userId is the same
  const chartData = useMemo(
    () => ({
      userId,
      series: computeSeriesData(userId),
    }),
    [userId]
  )

  return (
    <div>
      <input value={filter} onChange={e => setFilter(e.target.value)} />
      <Chart data={chartData} />
    </div>
  )
}
Note
`React.memo` and `useMemo` are a team: `React.memo` makes a component skip re-rendering when its props are the same, and `useMemo` ensures those props actually *are* the same reference between renders. Without `useMemo`, `React.memo` does nothing for object/array props.
Deriving Complex Data from Multiple Inputs

JSX
import { useMemo } from 'react'

function SalesReport({ transactions, startDate, endDate, category }) {
  // Computed from four different inputs — recompute only when any changes
  const summary = useMemo(() => {
    const filtered = transactions.filter(tx => {
      const d = new Date(tx.date)
      return (
        d >= startDate &&
        d <= endDate &&
        (category === 'all' || tx.category === category)
      )
    })

    const total = filtered.reduce((sum, tx) => sum + tx.amount, 0)
    const byCategory = filtered.reduce((acc, tx) => {
      acc[tx.category] = (acc[tx.category] || 0) + tx.amount
      return acc
    }, {})

    return { filtered, total, byCategory }
  }, [transactions, startDate, endDate, category])

  return (
    <div>
      <p>Total: ${summary.total.toFixed(2)}</p>
      <p>Transactions: {summary.filtered.length}</p>
    </div>
  )
}
When NOT to Use useMemo

useMemo has a cost: React must allocate memory for the cached value, store the dependencies, and compare them on every render. For simple computations, this overhead exceeds the savings.

  • Simple arithmeticconst total = price * quantity does not need memoization

  • String concatenationconst fullName = ${first} ${last}`` is fast enough

  • Components that rarely re-render — if the parent barely re-renders, the savings are minimal

  • First renderuseMemo only saves on subsequent renders; the first render always runs the factory

Warning
Don't pre-emptively memoize everything. Measure first. Use React DevTools Profiler to identify which components are slow and why, then apply `useMemo` strategically to the proven bottlenecks.
How to Measure if useMemo Helps

Use the React DevTools Profiler:

  • Open React DevTools → Profiler tab

  • Click "Record" and interact with your app

  • Stop recording and look at the flame chart

  • Components shown in orange/red are expensive

  • Check "Why did this render?" to see if props changed

  • Add useMemo to stabilize props that are unnecessarily re-created

  • Record again to confirm the improvement

Scenario

Use useMemo?

Filter/sort/transform a list of 10,000+ items

Yes — likely worth it

Build an object passed to React.memo child

Yes — ensures referential stability

Derive complex data from multiple expensive inputs

Yes — profile first

const doubled = count * 2

No — trivially fast

const label = isActive ? "Active" : "Inactive"

No — no real cost

Function that is passed to a child

Use useCallback instead

Tip
The React compiler (available in React 19+) can automatically apply memoization where it is safe to do so, reducing the need to manually write `useMemo`. If you are on React 19 with the compiler enabled, let it handle most memoization automatically.