Stopping Redundant Calculations with React useMemo
Learn how to use the useMemo hook to prevent expensive calculations from running on every render, improving React application performance and referential stability.
19 Jan 2026, 08:52 UTC

The Cost of Re-rendering
In a React application, every state update triggers a re-render of the component and its children. While React is generally fast, a problem arises when a component contains a "heavy" computation—such as filtering a list of 2,000 items or processing a complex data transformation. Without optimization, that heavy logic runs on every single render, even if the data being processed hasn't changed.
The useMemo hook solves this by caching (memoizing) the result of a calculation. Instead of running the logic every time, React stores the result and only re-calculates it when specific dependencies change.
When to Apply Memoization
Not every function needs useMemo. Using it everywhere adds memory overhead and increases the complexity of your dependency arrays. Focus on these two primary scenarios:
- Expensive Computations: Operations with high time complexity (e.g.,
.filter(),.reduce(), or.sort()on large arrays) that would cause noticeable lag in the UI. - Referential Stability: When you pass an object or array as a prop to a child component wrapped in
React.memo. Since{} !== {}in JavaScript, creating a new object on every render triggers a child re-render even if the data is identical.useMemoensures the reference remains the same.
Practical Implementation: Filtering a Dataset
Consider a component that filters a large list of users based on a search query. Without useMemo, typing in a separate "Theme Toggle" button would cause the entire list to be re-filtered, despite the user list and query remaining unchanged.
import React, { useState, useMemo } from 'react';
const UserList = ({ users }) => {
const [query, setQuery] = useState('');
const [isDarkMode, setIsDarkMode] = useState(false);
// This calculation 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 (
setIsDarkMode(!isDarkMode)}>Toggle Theme
setQuery(e.target.value)}
placeholder="Search users..."
/>
{filteredUsers.map(user => (- {user.name}
))}
);
};
Verification Steps
To verify this is working as intended, run the component and observe the console:
- Type in the search box: You should see "Filtering users..." logged for every keystroke.
- Click the "Toggle Theme" button: The UI should update, but the console should not log "Filtering users..." because the
isDarkModestate is not in theuseMemodependency array.
Trade-offs and Limitations
useMemo is a performance hint, not a hard guarantee. React may choose to discard the cached value to reclaim memory, meaning your function must be idempotent (it should produce the same output for the same input regardless of whether it is cached).
Avoid these common pitfalls:
- Using it for Side Effects: Never put API calls or DOM manipulations inside
useMemo. UseuseEffectfor those operations. - Over-memoizing: For simple operations like adding two numbers or concatenating a short string, the cost of React managing the dependency array is often higher than the cost of the calculation itself.
Decision Summary
Before adding useMemo, ask: Is this calculation causing a visible frame drop, or is it breaking a child's React.memo optimization? If the answer is no, keep the code simple. If the answer is yes, wrap the logic in useMemo and explicitly define the dependencies that trigger a recalculation.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.