Stop Over-Memoizing: When to Actually Use useMemo in React
Stop wrapping every variable in useMemo. Learn how to identify truly expensive computations and use referential equality to prevent unnecessary React re-renders.
19 May 2026, 02:04 UTC

The Cost of 'Just in Case' Optimization
A common pattern in React development is wrapping every derived value in useMemo. The logic seems sound: why calculate something twice if you can store it? However, memoization isn't free. Every time you use useMemo, React must allocate memory for the stored value and perform a shallow comparison of every item in the dependency array during every single render.
When the cost of that comparison and memory overhead exceeds the cost of the actual calculation, useMemo becomes a performance penalty rather than an optimization. The goal is to identify the specific scenarios where the computation is truly "expensive" or where referential stability is required to prevent a cascade of child re-renders.
Identifying Expensive Computations
An "expensive" computation is typically one that scales linearly or exponentially with the size of your data. Simple string concatenations or basic math are negligible. You should consider useMemo when dealing with:
- Filtering or sorting large arrays (e.g., 1,000+ items).
- Complex data transformations (e.g., converting a raw API response into a nested tree structure for a UI component).
- Heavy regex operations on large blocks of text.
If you aren't sure, use the React DevTools Profiler. If a component's "Actual Duration" is high, but the state changes are unrelated to the heavy calculation, that is your primary candidate for memoization.
Maintaining Referential Equality
Beyond raw speed, useMemo is critical for referential equality. In JavaScript, [] === [] is false. Every time a component renders, any object or array declared in the body is a brand new instance in memory.
If you pass a newly created array as a prop to a child component wrapped in React.memo, that child will re-render every time the parent does, regardless of whether the array's contents actually changed. useMemo ensures the reference remains the same across renders unless the dependencies change, effectively "silencing" unnecessary child updates.
Worked Example: Filtering a Large Dataset
Consider a component that filters a list of users based on a search query. Without useMemo, the filter runs on every keystroke AND every time any other state (like a theme toggle) changes.
import React, { useState, useMemo } from 'react';
function UserList({ users }) {
const [query, setQuery] = useState('');
const [darkMode, setDarkMode] = useState(false);
// This calculation runs on every render, even when only darkMode changes
// const filteredUsers = users.filter(user => user.name.includes(query));
// Optimized: Only runs when 'users' or 'query' changes
const filteredUsers = useMemo(() => {
console.log('Filtering users...'); // Diagnostic check
return users.filter(user => user.name.toLowerCase().includes(query.toLowerCase()));
}, [users, query]);
return (
<div style={{ background: darkMode ? '#333' : '#fff' }}
>
<button onClick={() => setDarkMode(!darkMode)}>Toggle Theme</button>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
<ul>
{filteredUsers.map(u => <li key={u.id}>{u.name}</li>)}
</ul>
</div>
);
}
Verification Steps
- Run the code above with the
console.loginside theuseMemoblock. - Type in the search input: You should see "Filtering users..." in the console.
- Click the "Toggle Theme" button: The console should remain silent, proving the filtered list was retrieved from cache rather than recalculated.
Trade-offs and Limitations
The most significant risk with useMemo is the stale closure. If you omit a variable from the dependency array that is used inside the factory function, the hook will continue to return a value based on the initial render's data, leading to UI bugs that are difficult to trace.
Additionally, useMemo is a performance hint, not a guarantee. React may choose to discard the memoized value to free up memory, meaning your code must be able to handle the calculation running again regardless of the hook.
Practical Decision Matrix
| Scenario | Use useMemo? | Reasoning |
|---|---|---|
| Simple variable derivation | No | Overhead > Calculation cost. |
| Filtering 5,000 rows | Yes | Prevents UI lag on unrelated renders. |
| Passing array to React.memo child | Yes | Preserves referential equality. |
| Triggering an API call | No | Use useEffect for side effects. |
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.