React Performance: memo, useMemo, & useCallback

Stop wasting renders. Master the core React hooks that memoize components, values, and functions to make your applications fast and efficient.

Lesson ProgressStep 1 of 11

My App

0 EXP

Welcome! Let's optimize a React app. Here's a simple app with a counter and a Header component.

function App() {
  const [count, setCount] = useState(0);
  return (
    <>
      <Header title="My App" />
      <button onClick={() => setCount(c => c + 1)}>
        Count: {count}
      </button>
    </>
  );
}

The Problem: Unnecessary Re-renders

By default, React re-renders a component whenever its parent re-renders. This happens even if the child component's props haven't changed.

function App() {
  const [count, setCount] = useState(0);
  
  // When 'count' changes, App re-renders,
  // which causes 'Child' to re-render too.
  return (
    <>
      <button onClick={() => setCount(c + 1)}>
        Count: {count}
      </button>
      <ChildComponent />
    </>
  );
}

For complex components, this is wasteful and can lead to performance issues. The goal of optimization is to tell React, "Don't re-render this component if its props are the same."

System Check

Why does the ChildComponent re-render when the button in App is clicked?

Advanced Holo-Simulations

0 EXP

Log in to unlock these advanced training modules and test your skills.


Achievements

📦
Memo Master

Correctly apply React.memo to prevent component re-renders.

🧠
Calculation Whiz

Memoize an expensive calculation using useMemo.

useCallback(…)
Callback Expert

Optimize a function prop with useCallback.

Mission: Fix the Wasted Renders

The `ChildComponent` re-renders every time the 'Click Me' button is pressed. This is a waste. Your mission is to use **React.memo** and **useCallback** to ensure the child only re-renders when its *actual* props change (which they don't).

A.D.A. Feedback:

> Awaiting input...

Challenge: Match the Optimization Tool

Drag the tools on the left to match their correct descriptions on the right.

useMemo
React.memo
useCallback
Wraps a component to prevent re-renders if props are the same.
Caches the result of an expensive calculation between renders.
Caches a function definition to maintain referential equality.

Challenge: Complete the Syntax

Fill in the missing parts of the hooks to optimize this component.

function MyComponent({ dataToSort, onSort }) {
// This sort is expensive
const sortedData = (() => {
return dataToSort.sort();
}, );

// This function is a prop
const handleSort = (() => {
onSort();
}, [onSort]);
}

Consult A.D.A.

Community Holo-Net

Peer Project Review

Submit your "Optimized Component" project for feedback from other Net-Runners.

A Deep Dive into React Performance

In React, a component re-rendering is not inherently bad; it's how the library works. However, *unnecessary* re-renders—when a component's DOM output doesn't change—can add up, leading to sluggish UIs and a poor user experience. Your goal as a developer isn't to stop all re-renders, but to eliminate the wasteful ones. This is where React's optimization tools come in.

Part 1: `React.memo` - Stopping Component Re-renders

`React.memo` is a Higher-Order Component (HOC) that performs a simple function: it **prevents a component from re-rendering if its props haven't changed.**

By default, `React.memo` performs a **shallow comparison** of the props object. This means it checks if `prevProps.foo === nextProps.foo` for every prop. This works great for simple values like strings, numbers, and booleans. It fails, however, for objects, arrays, and functions, which are re-created (and thus have a new reference) on every render.

❌ Problem

function App() {
  const [count, setCount] = useState(0);
  // This child will re-render 
  // when 'count' changes.
  return <ChildComponent data={...} />;
}

✔️ Solution

const MemoizedChild = React.memo(ChildComponent);

function App() {
  const [count, setCount] = useState(0);
  // This child will NOT re-render.
  return <MemoizedChild data={...} />;
}

Part 2: `useMemo` - Caching Expensive Calculations

The `useMemo` hook is for a different problem. It doesn't memoize a component; it memoizes a **value**.

Imagine you have a function that sorts a 10,000-item list or performs a complex mathematical calculation. If this function runs inside your component, it will re-run on *every single render*, even if its inputs didn't change. `useMemo` solves this by caching the function's return value and only re-running the function if one of its **dependencies** has changed.

// Before: Runs on every render
const sortedList = expensiveSort(list);

// After: Only runs when 'list' changes
const sortedList = useMemo(() => {
  return expensiveSort(list);
}, [list]);

Part 3: `useCallback` - The Key to `React.memo`

This is where most developers get stuck. You've wrapped your component in `React.memo`, but it's *still* re-rendering. Why?

The problem is likely a function prop.

const MemoizedChild = React.memo(ChildComponent);

function App() {
  // This function is RE-CREATED on every render
  const handleClick = () => { ... }

  // 'handleClick' is a new prop every time,
  // which breaks React.memo!
  return <MemoizedChild onClick={handleClick} />;
}

`useCallback` solves this by memoizing the **function itself**. It returns the *same function reference* between renders, as long as its dependencies don't change. This satisfies `React.memo`'s shallow comparison.

const MemoizedChild = React.memo(ChildComponent);

function App() {
  // This function reference is STABLE
  const handleClick = useCallback(() => { ... }, []);

  // 'handleClick' is the same prop every time.
  // React.memo now works!
  return <MemoizedChild onClick={handleClick} />;
}

Part 4: Profiling - Don't Guess, Measure!

The most important rule of optimization is: **do not optimize prematurely.** Adding `memo`, `useMemo`, and `useCallback` everywhere adds complexity and consumes memory. It can even make your app slower.

Always use the **React DevTools Profiler** first. It will show you which components are re-rendering and why. Focus your efforts on the components that are demonstrably slow.

Key Takeaway: Profile first. Use `React.memo` to stop component re-renders. If `memo` fails, use `useCallback` for function props and `useMemo` for complex object/array props. Use `useMemo` by itself to cache expensive calculations.

React Optimization Glossary

Memoization
An optimization technique that stores the result of an expensive function call in a cache. If the function is called again with the same inputs, the cached result is returned instead of re-computing it.
Re-render
The process of React calling a component's render function to determine if its UI needs to be updated. This happens when props or state change.
Referential Equality
In JavaScript, objects, arrays, and functions are compared by their reference (location in memory), not their content. Two identical objects created separately (e.g., `{{}} !== {{}}`) are not equal. This is the problem `useMemo` and `useCallback` solve.
Shallow Comparison
The default comparison method used by `React.memo` and React's pure components. It checks if `prevProps.key === nextProps.key` for all keys. It does not check for deep equality inside objects or arrays.
Higher-Order Component (HOC)
An advanced pattern in React where a function takes a component as an argument and returns a new, enhanced component. `React.memo` is a HOC.
`React.memo`
A HOC that memoizes a component. React will skip re-rendering the component if its props (checked via shallow comparison) have not changed.
`useMemo`
A React Hook that memoizes a **value**. It takes a function and a dependency array. It re-runs the function only if a dependency has changed, otherwise returning the cached value.
`useCallback`
A React Hook that memoizes a **function**. It returns a stable reference to that function, which only changes if a dependency in its dependency array changes.
Dependency Array
The second argument to `useMemo`, `useCallback`, and `useEffect`. It's an array of values that the hook "watches." If any value in the array changes between renders, the hook re-executes.
Profiler
A tool in the React DevTools that allows you to measure the performance of your application, identify "what" rendered and "why," and find performance bottlenecks.

About the Author

Author's Avatar

TodoTutorial Team

Passionate developers and educators making programming accessible to everyone.

This article was written and reviewed by our team of senior React developers, who have extensive experience in building and optimizing large-scale React applications.

Verification and Updates

Last reviewed: October 2025.

We strive to keep our content accurate and up-to-date. This tutorial is based on the latest React (v18+) specifications and is periodically reviewed to reflect industry best practices.

External Resources

Found an error or have a suggestion? Contact us!