Memoization
Problem
Components recalculate expensive operations on every render, even when inputs haven’t changed. This causes visible lag during typing, scrolling, or interactions. Sorting large datasets, complex filtering, or data transformations happen dozens of times per second unnecessarily.
Solution
Cache expensive computation results and reuse them when inputs haven’t changed. This skips redundant work and improves performance for calculations that would otherwise run on every render.
Example
This example demonstrates a memoization function that caches computation results based on function arguments, avoiding redundant calculations when called with the same inputs.
// Framework-agnostic memoization
function memoize(fn) {
// Store cached results in a Map
const cache = new Map();
return function(...args) {
// Create cache key from arguments
const key = JSON.stringify(args);
// Return cached result if available
if (cache.has(key)) {
return cache.get(key);
}
// Compute result if not cached
const result = fn(...args);
// Store result in cache for future calls
cache.set(key, result);
return result;
};
}
// Usage: wrap expensive function with memoization
const filterProducts = memoize((products, filter) => {
return products.filter(p => p.category === filter);
});
// First call computes and caches result
const filtered = filterProducts(products, 'electronics');
// Subsequent calls with same args return cached result instantly
Benefits
- Dramatically improves performance by avoiding expensive recalculations.
- Eliminates visible lag during user interactions with heavy computations.
- Reduces wasted CPU cycles for operations with identical inputs.
- Makes expensive operations viable in render functions.
Tradeoffs
- Adds memory overhead to store cached results.
- Can actually hurt performance if used on cheap operations due to comparison cost.
- Makes code harder to reason about with implicit caching behavior.
- Requires careful dependency management to avoid stale cached values.