Building Your Learning Module...
Getting things ready for you!
Find videos you like?
Save to resource drawer for future reference!
useMemo returns a memoized value. It caches the result of an expensive calculation between re-renders until its dependencies change, preventing unnecessary recalculations.
Expensive calculation runs on every re-render!
Calculation only runs when data changes!
While both hooks optimize performance, they serve different purposes. useMemo memoizes values, while useCallback memoizes functions.
Caches calculated values
Returns the result of a function
Used for expensive computations
Caches function instances
Returns the function itself
Used for callback functions
First, import useMemo from React at the top of your component file.
Pro Tip
You can import multiple hooks: { useState, useEffect, useMemo }.
Wrap your expensive calculation with useMemo to cache the result.
Key Point
The calculation only runs when dependencies in the array change!
Include only the variables that your calculation depends on.
Important
Missing dependencies can cause stale data. Too many dependencies reduce optimization benefits!
Use the memoized value in your component's JSX.
Key Point
The filtered list is only recalculated when the users array changes!
Filter and calculate statistics efficiently
function DataProcessor() {
const [products, setProducts] = React.useState([
{ id: 1, name: 'Laptop', price: 999, category: 'Electronics' },
{ id: 2, name: 'Book', price: 29, category: 'Education' },
{ id: 3, name: 'Phone', price: 699, category: 'Electronics' },
{ id: 4, name: 'Course', price: 199, category: 'Education' },
{ id: 5, name: 'Tablet', price: 399, category: 'Electronics' }
]);
const [selectedCategory, setSelectedCategory] = React.useState('All');
const [minPrice, setMinPrice] = React.useState(0);
const [rerender, setRerender] = React.useState(0);
// Memoized filtered products - only recalculates when filters change
const filteredProducts = React.useMemo(() => {
console.log('🔄 Filtering products...');
return products.filter(product => {
const categoryMatch = selectedCategory === 'All' || product.category === selectedCategory;
const priceMatch = product.price >= minPrice;
return categoryMatch && priceMatch;
});
}, [products, selectedCategory, minPrice]);
// Memoized statistics - only recalculates when filtered products change
const statistics = React.useMemo(() => {
console.log('📊 Calculating statistics...');
const total = filteredProducts.reduce((sum, product) => sum + product.price, 0);
const average = filteredProducts.length > 0 ? total / filteredProducts.length : 0;
// Fix Math.max/Math.min with empty arrays
const prices = filteredProducts.map(p => p.price);
const maxPrice = prices.length > 0 ? Math.max(...prices) : 0;
const minPriceActual = prices.length > 0 ? Math.min(...prices) : 0;
return {
total,
average: average.toFixed(2),
maxPrice,
minPrice: minPriceActual,
count: filteredProducts.length
};
}, [filteredProducts]);
// Memoized categories list - only recalculates when products change
const categories = React.useMemo(() => {
console.log('📋 Extracting categories...');
const cats = ['All', ...new Set(products.map(p => p.category))];
return cats;
}, [products]);
return (
<div className="container">
<h1>📊 useMemo Data Processing</h1>
<div className="controls">
<div className="filter-group">
<label>Category:</label>
<select
value={selectedCategory}
onChange={(e) => setSelectedCategory(e.target.value)}
>
{categories.map(cat => (
<option key={cat} value={cat}>{cat}</option>
))}
</select>
</div>
<div className="filter-group">
<label>Min Price: {'$'}{minPrice}</label>
<input
type="range"
min="0"
max="1000"
value={minPrice}
onChange={(e) => setMinPrice(Number(e.target.value))}
/>
</div>
<button onClick={() => setRerender(r => r + 1)} className="btn-secondary">
Force Re-render ({rerender})
</button>
</div>
<div className="stats-grid">
<div className="stat-card">
<h3>Total Products</h3>
<div className="stat-value">{statistics.count}</div>
</div>
<div className="stat-card">
<h3>Total Value</h3>
<div className="stat-value">{'$'}{statistics.total}</div>
</div>
<div className="stat-card">
<h3>Average Price</h3>
<div className="stat-value">{'$'}{statistics.average}</div>
</div>
<div className="stat-card">
<h3>Price Range</h3>
<div className="stat-value">{'$'}{statistics.minPrice} - {'$'}{statistics.maxPrice}</div>
</div>
</div>
<div className="products">
<h3>Filtered Products ({filteredProducts.length})</h3>
{filteredProducts.map(product => (
<div key={product.id} className="product-card">
<div className="product-info">
<h4>{product.name}</h4>
<p>{product.category}</p>
</div>
<div className="product-price">
{'$'}{product.price}
</div>
</div>
))}
</div>
<div className="info">
💡 Check the console to see when calculations run. They only run when dependencies change!
</div>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<DataProcessor />);Loading preview...
Returns same computed value between renders until dependencies change.
Prevents expensive calculations from running on every render.
Calculation only runs when dependencies in the array change.
Don't overuse! Only for truly expensive calculations.