useMemo returns a memoized value. It only recomputes the value when one of the dependencies has changed, helping to avoid expensive calculations on every render.
Signature
function useMemo<T>(
factory: () => T,
inputs: ReadonlyArray<unknown> | undefined
): T
Parameters
A function that computes and returns the value to be memoized. This function should be pure and take no arguments.
inputs
ReadonlyArray<unknown> | undefined
required
An array of dependencies. The memoized value will only be recomputed when one of these values changes (compared using ===). If undefined is passed, a new value will be computed whenever a new function instance is passed as the first argument.
Returns
Returns the memoized value of type T. On the initial render, it returns the result of calling factory(). On subsequent renders, it returns the cached value if dependencies haven’t changed, or recomputes by calling factory() if they have.
Basic Usage
import { useMemo, useState } from 'preact/hooks';
function ExpensiveComponent({ items }) {
const [filter, setFilter] = useState('');
// Only recompute when items or filter changes
const filteredItems = useMemo(() => {
console.log('Filtering items...');
return items.filter(item =>
item.name.toLowerCase().includes(filter.toLowerCase())
);
}, [items, filter]);
return (
<div>
<input value={filter} onChange={e => setFilter(e.target.value)} />
<ul>
{filteredItems.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
}
Expensive Calculations
function DataAnalysis({ data }) {
const statistics = useMemo(() => {
console.log('Computing statistics...');
return {
mean: data.reduce((sum, val) => sum + val, 0) / data.length,
max: Math.max(...data),
min: Math.min(...data),
sum: data.reduce((sum, val) => sum + val, 0)
};
}, [data]);
return (
<div>
<p>Mean: {statistics.mean}</p>
<p>Max: {statistics.max}</p>
<p>Min: {statistics.min}</p>
<p>Sum: {statistics.sum}</p>
</div>
);
}
Memoizing Objects
function UserProfile({ userId, userName }) {
// Prevent creating a new object on every render
const user = useMemo(() => ({
id: userId,
name: userName,
lastSeen: Date.now()
}), [userId, userName]);
return <ExpensiveChildComponent user={user} />;
}
Sorting and Filtering
function ProductList({ products, sortBy, searchTerm }) {
const displayedProducts = useMemo(() => {
let result = products;
// Filter
if (searchTerm) {
result = result.filter(p =>
p.name.toLowerCase().includes(searchTerm.toLowerCase())
);
}
// Sort
result = [...result].sort((a, b) => {
if (sortBy === 'price') return a.price - b.price;
if (sortBy === 'name') return a.name.localeCompare(b.name);
return 0;
});
return result;
}, [products, sortBy, searchTerm]);
return (
<ul>
{displayedProducts.map(product => (
<li key={product.id}>{product.name} - ${product.price}</li>
))}
</ul>
);
}
Computing Derived State
function ShoppingCart({ items }) {
const summary = useMemo(() => {
const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
const tax = subtotal * 0.1;
const shipping = subtotal > 100 ? 0 : 10;
const total = subtotal + tax + shipping;
return { subtotal, tax, shipping, total };
}, [items]);
return (
<div>
<p>Subtotal: ${summary.subtotal.toFixed(2)}</p>
<p>Tax: ${summary.tax.toFixed(2)}</p>
<p>Shipping: ${summary.shipping.toFixed(2)}</p>
<p>Total: ${summary.total.toFixed(2)}</p>
</div>
);
}
Referential Equality
import { memo } from 'preact/compat';
// Child component only re-renders if style object reference changes
const ExpensiveChild = memo(({ style }) => {
console.log('Child rendered');
return <div style={style}>Expensive component</div>;
});
function Parent({ color }) {
// Memoize style object to prevent unnecessary child re-renders
const style = useMemo(() => ({
backgroundColor: color,
padding: '20px',
borderRadius: '8px'
}), [color]);
return <ExpensiveChild style={style} />;
}
Creating Regex Patterns
function SearchableList({ items, pattern }) {
const regex = useMemo(() => {
try {
return new RegExp(pattern, 'i');
} catch {
return null;
}
}, [pattern]);
const filteredItems = useMemo(() => {
if (!regex) return items;
return items.filter(item => regex.test(item.name));
}, [items, regex]);
return (
<ul>
{filteredItems.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
Graph Computations
function SocialGraph({ users, connections }) {
const adjacencyList = useMemo(() => {
const graph = new Map();
users.forEach(user => {
graph.set(user.id, new Set());
});
connections.forEach(([from, to]) => {
graph.get(from)?.add(to);
graph.get(to)?.add(from);
});
return graph;
}, [users, connections]);
const getMutualFriends = (userId1, userId2) => {
const friends1 = adjacencyList.get(userId1) || new Set();
const friends2 = adjacencyList.get(userId2) || new Set();
return [...friends1].filter(id => friends2.has(id));
};
return <div>{/* Use adjacencyList */}</div>;
}
With undefined Dependencies
function Component({ computeFn }) {
// Recompute whenever computeFn reference changes
const result = useMemo(() => {
return computeFn();
}, undefined);
return <div>{result}</div>;
}
useMemo is an optimization tool. Your code should work correctly without it, then add it to improve performance where needed.
Don’t use useMemo for everything. It has overhead, so only use it for computationally expensive operations or when maintaining referential equality is important for child component optimization.
Unlike React, Preact requires the inputs parameter. Pass undefined explicitly if you want to recompute whenever the factory function changes.