Paramclasses Academy - Blog

Tutorials Details

←
Post 20 of 29
→

What is React Performance Optimization — memo, useMemo, and useCallback ?

What is React Performance Optimization — memo, useMemo, and useCallback ?

October 30, 2026

1. Introduction

React is a JavaScript library used to build interactive user interfaces. As an application grows, it may contain hundreds of components, complex calculations, large lists, and multiple user interactions.

Sometimes, components render again even when their displayed information has not changed. Unnecessary rendering and repeated calculations can affect application performance.

React provides performance optimization techniques such as:

  • React.memo()

  • useMemo()

  • useCallback()

These tools help developers avoid certain unnecessary component renders, reuse calculated values, and preserve function references between renders.

Main Goal: Understand how React rendering works and learn how to optimize React applications efficiently.

2. Learning Objectives

After completing this tutorial, you will understand:

  1. What performance optimization means in React.

  2. What rendering and re-rendering are.

  3. What unnecessary re-rendering means.

  4. How React.memo() works.

  5. What the useMemo() Hook does.

  6. What the useCallback() Hook does.

  7. The difference between memo, useMemo, and useCallback.

  8. When performance optimization is useful.

  9. Common optimization mistakes and best practices.

3. Core Concepts and Explanation

3.1 What Is Performance Optimization?

Performance optimization is the process of improving an application's speed, responsiveness, and resource efficiency.

In React, performance optimization can involve reducing unnecessary component renders, avoiding repeated expensive calculations, and improving how data and functions are passed between components.

Example:

Imagine an e-commerce website with 500 product cards.

When a user changes a filter, you may want only the components that need updated information to perform additional work.

Optimization techniques can help reduce unnecessary work in such situations.

3.2 What Is Rendering in React?

Rendering is the process through which React calls components to determine what the user interface should look like.

A component may render again when:

  1. Its state changes.

  2. Its parent component renders again.

  3. Its props change.

  4. A context value it consumes changes.

Rendering does not automatically mean that React updates every DOM element. React compares the resulting UI and applies the necessary DOM changes.

3.3 What Is Unnecessary Re-rendering?

Unnecessary re-rendering happens when a component renders again even though its relevant data or output does not need to change.

For example:

import { useState } from "react";

function Child() {
  console.log("Child Rendered");

  return <h2>Hello React</h2>;
}

function App() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>Count: {count}</h1>

      <button onClick={() => setCount(count + 1)}>
        Increase
      </button>

      <Child />
    </div>
  );
}

export default App;

Explanation:

  • App contains a state variable named count.

  • Clicking the button updates count.

  • App renders again.

  • Child also renders again because it is not memoized.

The Child component does not use count, but it still renders when its parent renders.

3.4 What Is React.memo()?

React.memo() is a higher-order component used to memoize a functional component.

It can prevent a component from rendering again when its props have not changed according to React's default shallow comparison.

Syntax:

const MemoizedComponent = memo(Component);

Example:

import { memo, useState } from "react";

const Child = memo(function Child() {
  console.log("Child Rendered");

  return <h2>Hello React</h2>;
});

function App() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>Count: {count}</h1>

      <button onClick={() => setCount(count + 1)}>
        Increase
      </button>

      <Child />
    </div>
  );
}

export default App;

Explanation:

  • memo() wraps the Child component.

  • Child receives no changing props.

  • When App renders again, React can skip rendering Child.

  • This can reduce unnecessary rendering work.

Important: React.memo() is an optimization, not a guarantee. A memoized component can still render when its own state or consumed context changes.

3.5 What Is useMemo()?

useMemo() is a React Hook used to cache the result of a calculation between renders.

It recalculates the value when one of its dependencies changes.

Syntax:

const cachedValue = useMemo(() => {
  return calculateSomething();
}, [dependencies]);

Example:

import { useMemo, useState } from "react";

function App() {
  const [number, setNumber] = useState(5);
  const [count, setCount] = useState(0);

  const square = useMemo(() => {
    console.log("Calculation Running");

    return number * number;
  }, [number]);

  return (
    <div>
      <h2>Square: {square}</h2>

      <button onClick={() => setNumber(number + 1)}>
        Change Number
      </button>

      <button onClick={() => setCount(count + 1)}>
        Increase Count: {count}
      </button>
    </div>
  );
}

export default App;

Explanation:

  • number stores the value used in the calculation.

  • count stores a separate counter value.

  • useMemo() caches the square calculation.

  • When number changes, the calculation runs again.

  • When only count changes, React can reuse the cached square value.

useMemo() is most useful when a calculation is expensive or when a stable object or array reference is needed.

3.6 What Is useCallback()?

useCallback() is a React Hook used to cache a function definition between renders.

It returns the same function reference until one of its dependencies changes.

Syntax:

const cachedFunction = useCallback(() => {
  // Function logic
}, [dependencies]);

Example:

import { memo, useCallback, useState } from "react";

const Child = memo(function Child({ onClick }) {
  console.log("Child Rendered");

  return (
    <button onClick={onClick}>
      Click Child
    </button>
  );
});

function App() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    console.log("Child Button Clicked");
  }, []);

  return (
    <div>
      <h2>Count: {count}</h2>

      <button onClick={() => setCount(count + 1)}>
        Increase Count
      </button>

      <Child onClick={handleClick} />
    </div>
  );
}

export default App;

Explanation:

  • handleClick is a function.

  • useCallback() preserves its reference between renders.

  • Child is wrapped with memo().

  • When count changes, handleClick keeps the same reference.

  • React can skip rendering Child because its prop has not changed.

3.7 Difference Between memo, useMemo, and useCallback

Feature Purpose
React.memo() Memoizes a component
useMemo() Memoizes a calculated value
useCallback() Memoizes a function reference

Easy way to remember:

  • memo = Component optimization.

  • useMemo = Value optimization.

  • useCallback = Function optimization.

3.8 Understanding Dependencies

Dependencies tell React when a cached value or function needs to be updated.

Example:

const result = useMemo(() => {
  return number * 2;
}, [number]);

Here, number is a dependency.

If number changes, React recalculates result.

If number remains unchanged, React can reuse the cached value.

The dependency array must include every reactive value used inside the calculation or callback, following React's Hook rules.

4. Practical Code Examples

Example 1: Optimizing a Child Component with memo()

import { memo, useState } from "react";

const User = memo(function User({ name }) {
  console.log("User Component Rendered");

  return <h2>User: {name}</h2>;
});

function App() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>Counter: {count}</h1>

      <button onClick={() => setCount(count + 1)}>
        Increase
      </button>

      <User name="Rahul" />
    </div>
  );
}

export default App;

The User component receives the same name prop, so React can skip its re-render when only the counter changes.

Example 2: Optimizing Expensive Calculations with useMemo()

import { useMemo, useState } from "react";

function App() {
  const [number, setNumber] = useState(10);

  const factorial = useMemo(() => {
    console.log("Calculating Factorial");

    let result = 1;

    for (let i = 1; i <= number; i++) {
      result *= i;
    }

    return result;
  }, [number]);

  return (
    <div>
      <h2>Number: {number}</h2>

      <h2>Factorial: {factorial}</h2>

      <button onClick={() => setNumber(number + 1)}>
        Increase Number
      </button>
    </div>
  );
}

export default App;

The factorial is recalculated when number changes. Other renders can reuse the cached result.

Example 3: Optimizing Function References with useCallback()

import { memo, useCallback, useState } from "react";

const Button = memo(function Button({ onClick }) {
  console.log("Button Rendered");

  return (
    <button onClick={onClick}>
      Click Me
    </button>
  );
});

function App() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    alert("Button Clicked");
  }, []);

  return (
    <div>
      <h2>Count: {count}</h2>

      <button onClick={() => setCount(count + 1)}>
        Increase Count
      </button>

      <Button onClick={handleClick} />
    </div>
  );
}

export default App;

The function reference remains stable, allowing the memoized Button component to skip renders caused only by the parent's count update.

Example 4: Using useMemo() for Filtering Products

import { useMemo, useState } from "react";

const products = [
  { id: 1, name: "Laptop", price: 50000 },
  { id: 2, name: "Mobile", price: 20000 },
  { id: 3, name: "Keyboard", price: 2000 },
  { id: 4, name: "Monitor", price: 15000 }
];

function App() {
  const [search, setSearch] = useState("");
  const [count, setCount] = useState(0);

  const filteredProducts = useMemo(() => {
    return products.filter((product) =>
      product.name.toLowerCase().includes(search.toLowerCase())
    );
  }, [search]);

  return (
    <div>
      <input
        type="text"
        placeholder="Search Products"
        value={search}
        onChange={(e) => setSearch(e.target.value)}
      />

      <button onClick={() => setCount(count + 1)}>
        Counter: {count}
      </button>

      {filteredProducts.map((product) => (
        <p key={product.id}>
          {product.name} - ₹{product.price}
        </p>
      ))}
    </div>
  );
}

export default App;

Explanation:

  • products contains the product data.

  • search stores the user's search text.

  • useMemo() caches the filtered product list.

  • Filtering runs again when search changes.

  • Changing count does not require the filtering calculation to run again.

5. Real-World Applications

E-commerce Applications

  • Optimizing product lists.

  • Filtering and sorting products.

  • Preventing unnecessary product-card renders.

  • Improving shopping cart interfaces.

Admin Dashboards

  • Optimizing large data tables.

  • Caching calculated statistics.

  • Reducing unnecessary chart calculations.

  • Improving dashboard responsiveness.

Social Media Applications

  • Optimizing post components.

  • Preventing unnecessary profile renders.

  • Improving comment and notification lists.

Learning Management Systems

  • Optimizing course lists.

  • Caching progress calculations.

  • Improving quiz and result interfaces.

6. Common Mistakes and Best Practices

Common Mistakes

  1. Using memo() on every component without a reason.

  2. Using useMemo() for very simple calculations unnecessarily.

  3. Using useCallback() for every function.

  4. Forgetting dependencies in dependency arrays.

  5. Passing newly created objects or arrays to memoized components.

  6. Expecting memoization to prevent every render.

  7. Ignoring actual performance measurements.

Best Practices

  1. Identify performance problems before optimizing.

  2. Use React Developer Tools Profiler to identify expensive renders.

  3. Use React.memo() when a component frequently receives the same props and rendering it is costly.

  4. Use useMemo() for expensive calculations.

  5. Use useCallback() when a stable function reference is useful, especially with memoized children.

  6. Keep dependency arrays accurate.

  7. Avoid unnecessary state updates.

  8. Keep components focused and reasonably small.

  9. Measure performance before and after optimization.

  10. Prefer readable code over unnecessary optimization.

Important Note: Memoization has its own cost. It is not automatically faster to use memo, useMemo, or useCallback everywhere.

7. Practice Exercises

Exercise 1: Create a Counter component and use React.memo() to optimize a child component.

Exercise 2: Create a number calculator using useMemo().

Exercise 3: Create a button component and pass a function using useCallback().

Exercise 4: Create a product search application and optimize product filtering using useMemo().

Exercise 5: Create a parent component with multiple child components and identify which components render again.

Exercise 6: Use React Developer Tools Profiler to inspect component rendering and identify unnecessary work.

8. Key Takeaways and Conclusion

In this tutorial, we learned how to optimize React application performance using memoization techniques.

Key takeaways:

  • Rendering is how React determines the UI for a component.

  • Unnecessary re-rendering can create avoidable work.

  • React.memo() can skip rendering a component when its props remain unchanged.

  • useMemo() caches calculated values.

  • useCallback() caches function references.

  • Dependency arrays determine when cached values and functions are updated.

  • Performance optimization should be based on actual application needs.

Final Conclusion:

React performance optimization is an important skill for professional React developers. Understanding memo(), useMemo(), and useCallback() helps developers build efficient, responsive, and scalable applications.

Remember: Do not optimize everything blindly. First identify the performance problem, then apply the appropriate optimization technique.

←
Post 20 of 29
→