Avoiding Unnecessary Re-Renders
React re-renders a component whenever its state or props change. In most cases this is fast enough, but for large lists or expensive computations, unnecessary re-renders can cause performance issues.
React provides React.memo, useMemo, and useCallback to optimize performance by preventing unnecessary work.
Example
import { useState, useMemo, useCallback, memo } from 'react';
// React.memo — Prevents re-render if props haven't changed
const ExpensiveItem = memo(function ExpensiveItem({ item, onSelect }) {
console.log('Rendering:', item.name);
return (
<div onClick={() => onSelect(item.id)}>
{item.name}
</div>
);
});
function ItemList({ items }) {
const [filter, setFilter] = useState('');
const [selectedId, setSelectedId] = useState(null);
// useMemo — Cache expensive computation
const filteredItems = useMemo(() => {
return items.filter(item =>
item.name.toLowerCase().includes(filter.toLowerCase())
);
}, [items, filter]);
// useCallback — Cache function reference
const handleSelect = useCallback((id) => {
setSelectedId(id);
}, []);
return (
<div>
<input value={filter} onChange={e => setFilter(e.target.value)} />
{filteredItems.map(item => (
<ExpensiveItem
key={item.id}
item={item}
onSelect={handleSelect}
/>
))}
</div>
);
} - React.memo(Component) — Skips re-render if props are the same
- useMemo(fn, [deps]) — Caches the result of an expensive computation
- useCallback(fn, [deps]) — Caches a function reference
- Don't optimize prematurely — measure first, optimize when needed
- Use React DevTools Profiler to identify performance bottlenecks
Notes
- React is fast by default. Only add memo, useMemo, or useCallback when you identify actual performance issues. Premature optimization adds complexity without benefit.
