Skip to main content
React Introduction
CHAPTER 24 Beginner

React Performance Optimization

Updated: May 13, 2026
30 min read

# 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.memo to skip re-rendering static child components.
  • Use the useMemo hook to cache expensive calculations.
  • Use the useCallback hook 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> ); }

123
### 2. `useMemo` (Caching Values)
Sometimes a component does heavy math (like filtering an array of 10,000 items). If the component re-renders for an unrelated reason, the math runs again. `useMemo` caches the result of the math.

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 ); }

123
### 3. `useCallback` (Caching Functions)
When a Parent re-renders, it creates brand new functions in memory. If you pass that function as a prop to a `React.memo` Child, the Child thinks the prop changed (because the memory address changed) and re-renders anyway, breaking the memoization! `useCallback` fixes this by caching the function itself.

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} />; }

123456789101112131415161718192021222324
## 5. Output Explanations
Memoization is simply the concept of "caching." 
- `React.memo`: Caches the entire Component's HTML output.
- `useMemo`: Caches a JavaScript value (array, object, number).
- `useCallback`: Caches a JavaScript function.

## 6. Real-World Examples
- **`useMemo`:** Sorting a massive array of e-commerce products by price.
- **`React.memo`:** A complex interactive charting component (like Chart.js) that you only want to redraw if the actual data data array changes.

## 7. Common Mistakes
- **Premature Optimization:** Wrapping *every* component in `React.memo` and every function in `useCallback`. Caching takes memory and processing power. If the component is simple (like a button), the cost of checking the cache is actually *slower* than just letting React re-render it normally!
- **Missing Dependencies:** Forgetting to add variables to the dependency array of `useMemo` or `useCallback` will result in the cached functions using stale, outdated state.

## 8. Best Practices
- **Build first, optimize later.** Only reach for `useMemo` or `React.memo` if you visually notice your app lagging, or if you identify a bottleneck using the React Developer Tools Profiler.
- The best optimization is simply pushing state down. If only the Child needs the state, move the `useState` into the Child so the Parent never re-renders in the first place!

## 9. Exercises
1. Write a component that calculates the sum of all numbers from 1 to 10,000,000. Use `useMemo` to ensure this calculation only happens once.

## 10. Mini Project: Optimized Product Grid
Let's see an example of where `useMemo` is highly beneficial: filtering a large list of items while interacting with an unrelated state variable.

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.

16. Next Chapter Recommendation

With state management, routing, and performance covered, it's time to talk about securing access to parts of your application. In Chapter 25: React Authentication Basics, we will look at how to handle logins, tokens, and protected routes.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·