
React Performance Patterns: From Memo to Virtualization
Performance optimization in React applications requires a deep understanding of rendering behavior and various optimization techniques. In this article, we'll explore different performance patterns, from basic memoization to advanced virtualization techniques.
Understanding React's Rendering Behavior
Before diving into optimization patterns, let's understand when and why React components re-render:
1function ParentComponent() {
2 const [count, setCount] = useState(0);
3
4 return (
5 <div>
6 <Counter count={count} />
7 <ExpensiveComponent /> {/* Re-renders on every count change */}
8 </div>
9 );
10}
11
12function ExpensiveComponent() {
13 // Expensive calculations or rendering
14 return <div>{/* ... */}</div>;
15}Memoization Patterns
Using React.memo
1// Before optimization
2function MovieCard({ title, rating, onFavorite }: MovieCardProps) {
3 return (
4 <div className="card">
5 <h3>{title}</h3>
6 <div>{rating} stars</div>
7 <button onClick={onFavorite}>Favorite</button>
8 </div>Optimizing with useMemo
1function SearchResults({ items, query }: SearchResultsProps) {
2 const filteredItems = useMemo(() => {
3 console.log('Filtering items...'); // Expensive operation
4 return items.filter(item =>
5 item.title.toLowerCase().includes(query.toLowerCase())
6 );
7 }, [items, query]);
8useCallback for Event Handlers
1function ProductList({ products }: ProductListProps) {
2 const [favorites, setFavorites] = useState<Set<string>>(new Set());
3
4 const handleFavorite = useCallback((productId: string) => {
5 setFavorites(prev => {
6 const next = new Set(prev);
7 if (next.has(productId)) {
8 next.delete(productId);Debouncing and Throttling
Custom Debounce Hook
1function useDebounce<T>(value: T, delay: number): T {
2 const [debouncedValue, setDebouncedValue] = useState(value);
3
4 useEffect(() => {
5 const timer = setTimeout(() => {
6 setDebouncedValue(value);
7 }, delay);
8Custom Throttle Hook
1function useThrottle<T>(value: T, interval: number): T {
2 const [throttledValue, setThrottledValue] = useState(value);
3 const lastExecuted = useRef<number>(Date.now());
4
5 useEffect(() => {
6 const handler = setTimeout(() => {
7 const now = Date.now();
8 if (now >= lastExecuted.current + interval) {Virtualization Techniques
Basic Virtualization Hook
1interface UseVirtualizationProps {
2 itemCount: number;
3 itemHeight: number;
4 containerHeight: number;
5 overscan?: number;
6}
7
8function useVirtualization({Code Splitting and Lazy Loading
Route-Based Code Splitting
1import { lazy, Suspense } from 'react';
2
3const Dashboard = lazy(() => import('./pages/Dashboard'));
4const Profile = lazy(() => import('./pages/Profile'));
5const Settings = lazy(() => import('./pages/Settings'));
6
7function App() {
8 return (Component-Based Code Splitting
1const HeavyChart = lazy(() => import('./components/HeavyChart'));
2
3function Dashboard() {
4 const [showChart, setShowChart] = useState(false);
5
6 return (
7 <div>
8 <button onClick={() => setShowChart(true)}>Show Chart</button>Performance Monitoring
Custom Performance Hook
1function usePerformanceMonitor(componentName: string) {
2 const renderCount = useRef(0);
3 const lastRenderTime = useRef(performance.now());
4
5 useEffect(() => {
6 const renderTime = performance.now() - lastRenderTime.current;
7 renderCount.current += 1;
8Measuring Re-Renders
Best Practices and Guidelines
- When to Use Memoization
- For expensive calculations
- For preventing unnecessary re-renders
- When props are stable
1// Good use case
2const expensiveValue = useMemo(() => {
3 return someExpensiveCalculation(props.data);
4}, [props.data]);
5
6// Bad use case (over-optimization)
7const simpleValue = useMemo(() => {
8 return props.value + 1;
9}, [props.value]);2. Virtualization Considerations
- Use for large lists (100+ items)
- Consider variable height items
- Handle scroll restoration
3. Code Splitting Guidelines
- Split by route
- Split by feature
- Split by viewport visibility
4. Performance Testing
1describe('Performance Tests', () => {
2 it('should render list efficiently', async () => {
3 const startTime = performance.now();
4
5 render(<VirtualizedList items={largeDataSet} />);
6
7 const endTime = performance.now();
8 expect(endTime - startTime).toBeLessThan(100);
9 });
10});Conclusion
React performance optimization is a balance between:
- Code complexity
- Bundle size
- Runtime performance
- Development experience
Key takeaways:
- Use memoization judiciously
- Implement virtualization for large lists
- Split code based on user needs
- Monitor and measure performance
- Focus on user-perceived performance

