React Performance Optimization
# React Performance Optimization
1. Introduction
React is extremely fast out of the box thanks to the Virtual DOM. However, in large, complex applications with thousands of components, poor state management can cause the app to slow down or feel sluggish. In this chapter, we will learn how to identify performance bottlenecks and use React's built-in optimization tools to prevent unnecessary work.2. Learning Objectives
By the end of this chapter, you will be able to:- Understand when and why React re-renders components.
-
Use
React.memoto skip re-rendering static child components.
-
Use the
useMemohook to cache expensive calculations.
-
Use the
useCallbackhook to cache functions.
- Understand the dangers of "Premature Optimization".
3. Beginner-Friendly Explanations
The Golden Rule of Re-Renders
When a Parent component's state changes, React re-renders the Parent AND all of its Children components, even if the children's props didn't change! Usually, this is fine. React is very fast. But if a Child component is massive (like a data table with 1,000 rows), re-rendering it just because the Parent updated a tiny[text, setText] input field is a huge waste of CPU power.
4. Syntax Explanation
1. React.memo (Memoizing Components)
If a component's props have not changed, we can tell React to skip re-rendering it using React.memo().
```jsx id="ch24_ex1" import React, { useState } from 'react';
// Wrap the child component in React.memo() const ExpensiveChild = React.memo(({ title }) => { console.log("ExpensiveChild re-rendered!"); // Will only run if 'title' changes return <div className="p-4 bg-gray-200">Massive Table: {title}</div>; });
function Parent() { const [count, setCount] = useState(0);
return ( <div> <button onClick={() => setCount(c => c + 1)}>Count: {count}</button> {/* Even though Parent re-renders when count changes, ExpensiveChild is skipped! */} <ExpensiveChild title="User Data" /> </div> ); }
jsx id="ch24_ex2" import { useState, useMemo } from 'react';
function SearchList({ items }) { const [query, setQuery] = useState(""); const [darkMode, setDarkMode] = useState(false);
// This heavy filter ONLY re-runs if 'items' or 'query' changes. // Clicking the 'darkMode' toggle will NOT re-run this math! const filteredItems = useMemo(() => { console.log("Filtering array..."); return items.filter(item => item.includes(query)); }, [items, query]); // Dependency array!
return ( // ... UI rendering filteredItems null ); }
jsx id="ch24_ex3" import { useState, useCallback } from 'react';
function Parent() { const [count, setCount] = useState(0);
// Caches the function so its memory address stays the same across renders const handleDelete = useCallback(() => { console.log("Deleted!"); }, []); // Dependency array
return <MemoizedChild onDelete={handleDelete} />; }
jsx id="ch24_proj1" // Example React component import React, { useState, useMemo } from 'react';
// Mock data generation
const generateProducts = () => {
const items = [];
for (let i = 0; i < 5000; i++) {
items.push({ id: i, name: Product ${i}, price: Math.random() * 100 });
}
return items;
};
const products = generateProducts();
export default function OptimizedGrid() { const [query, setQuery] = useState(""); const [theme, setTheme] = useState("light"); // Unrelated state
// EXPENSIVE CALCULATION (Filtering 5000 items) // useMemo ensures this ONLY runs when 'query' changes. // Changing 'theme' will skip this math completely! const filteredProducts = useMemo(() => { console.log("Heavy filtering running..."); return products.filter(p => p.name.toLowerCase().includes(query.toLowerCase())); }, [query]);
return (
<div className={min-h-screen p-8 transition-colors ${theme === 'dark' ? 'bg-slate-900 text-white' : 'bg-slate-50 text-slate-900'}}>
<div className="max-w-4xl mx-auto">
<div className="flex justify-between items-center mb-8">
<h1 className="text-3xl font-bold">Inventory</h1>
<button
onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}
className="px-4 py-2 rounded bg-indigo-500 text-white font-bold hover:bg-indigo-600 transition"
>
Toggle Theme
</button>
</div>
<input type="text" value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Search 5000 products..." className="w-full p-4 rounded-xl border border-slate-300 text-slate-900 mb-6 shadow-sm focus:ring-2 focus:ring-indigo-500 outline-none" />
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{filteredProducts.slice(0, 20).map(product => ( // Only show first 20 for UI sake
<div key={product.id} className={p-4 rounded-lg shadow-sm border ${theme === 'dark' ? 'bg-slate-800 border-slate-700' : 'bg-white border-slate-200'}}>
<div className="font-bold">{product.name}</div>
<div className="text-green-500">${product.price.toFixed(2)}</div>
</div>
))}
</div>
<p className="mt-6 text-sm text-slate-500">Showing {filteredProducts.length} matches.</p>
</div>
</div>
);
}
``
11. Coding Challenges
Challenge 1: Wrap the individual product card inside the .map() function into a separate ProductCard component. Use React.memo() on it. Add a console.log inside it to prove it doesn't re-render when the theme changes.
12. MCQs with Answers
Q1: What is the primary purpose of the useMemo hook?
A) To cache a Component.
B) To cache a function definition.
C) To cache the result of an expensive calculation.
D) To store data in LocalStorage.
*Answer: C*
Q2: If a Parent component re-renders, what happens to its Child components by default?
A) They re-render only if their props changed.
B) They all re-render, regardless of props.
C) They do not re-render.
*Answer: B*
13. Interview Questions
-
Q: Explain the difference between
useMemo and useCallback.
-
Q: What is "Premature Optimization" and why is it bad in React?
14. FAQs
Is useMemo the same as useEffect?
No! useEffect is for side effects (like network calls) after rendering. useMemo is for returning a cached value *during* rendering.
15. Summary
React is fast by default, but you are in control of its maximum performance. By understanding how React's render cycle works, you can use React.memo, useMemo, and useCallback` to cache UI, data, and functions, ensuring your complex applications remain smooth and responsive.