DEV Community

Cover image for React Mastery Series – Day 15: React Performance Optimization – Understanding useCallback and React.memo
Siva Samanthapudi
Siva Samanthapudi

Posted on

React Mastery Series – Day 15: React Performance Optimization – Understanding useCallback and React.memo

Welcome back to the React Mastery Series!

In the previous article, we explored useRef and useMemo Hooks and learned how React helps developers optimize applications by:

  • Storing values without triggering re-renders
  • Accessing DOM elements
  • Memoizing expensive calculations
  • Improving rendering performance

Today, we will explore two important performance optimization concepts:

useCallback and React.memo

These concepts become extremely important when building:

  • Large enterprise applications
  • Complex dashboards
  • Data-heavy applications
  • Component libraries
  • High-performance React systems

Understanding React Re-rendering

Before learning optimization, we need to understand rendering behavior.

A React component re-renders when:

  • State changes
  • Props change
  • Parent component re-renders
  • Context value changes

Example:

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

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

      <Child />
    </>
  );
}

Enter fullscreen mode Exit fullscreen mode

Every time count changes:

State Update
      |
      ↓
Parent Re-renders
      |
      ↓
Child Re-renders
Enter fullscreen mode Exit fullscreen mode

Even though:

Child data did not change
Enter fullscreen mode Exit fullscreen mode

React still renders it.


What is React.memo?

React.memo is a higher-order component that prevents unnecessary re-renders.

It tells React:

Re-render this component only when its props change.

Syntax:

const ComponentName =
React.memo(Component);
Enter fullscreen mode Exit fullscreen mode

Example Without React.memo

Parent:

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

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

      <Child />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Child:

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

  return <h2>Child Component</h2>;
}
Enter fullscreen mode Exit fullscreen mode

Output:

Initial Load
Child Render
Click Button
Child Render
Child Render
Enter fullscreen mode Exit fullscreen mode

The child renders every time the parent updates.


Optimizing with React.memo

Example:

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

  return <h2>Child Component</h2>;
});
Enter fullscreen mode Exit fullscreen mode

Now:

Initial Load

Child Render


Parent State Changes

Child Does Not Render
Enter fullscreen mode Exit fullscreen mode

because its props did not change.


Understanding Props Comparison

React.memo performs a shallow comparison.

Example:

<Child name="Siva" />
Enter fullscreen mode Exit fullscreen mode

React compares:

Before:

{
 name:"Siva"
}
Enter fullscreen mode Exit fullscreen mode

After:

{
 name:"Siva"
}
Enter fullscreen mode Exit fullscreen mode

No change:

Skip Render
Enter fullscreen mode Exit fullscreen mode

Problem with Objects as Props

Consider:

function Parent() {
  const user = {
    name: "Siva",
  };

  return <Child user={user} />;
}
Enter fullscreen mode Exit fullscreen mode

Even though the data looks same:

{
name:"Siva"
}
Enter fullscreen mode Exit fullscreen mode

A new object is created every render.

React sees:

Previous Object
       ≠
New Object
Enter fullscreen mode Exit fullscreen mode

because references are different.

Result:

Child Re-renders
Enter fullscreen mode Exit fullscreen mode

Combining React.memo with useMemo

Solution:

const user = useMemo(
  () => ({
    name: "Siva",
  }),
  [],
);
Enter fullscreen mode Exit fullscreen mode

Now:

Object Reference Remains Same
        |
        ↓
React.memo Works Correctly
        |
        ↓
Child Avoids Re-render
Enter fullscreen mode Exit fullscreen mode

Understanding useCallback

Now let's explore:

useCallback

useCallback is used to memorize functions.

Syntax:

const memoizedFunction = useCallback(
  function,
  dependencies
);
Enter fullscreen mode Exit fullscreen mode

It returns the same function reference until dependencies change.


Why Do We Need useCallback?

Consider:

function Parent() {
  const handleClick = () => {
    console.log("Clicked");
  };

  return <Child onClick={handleClick} />;
}
Enter fullscreen mode Exit fullscreen mode

Every render creates a new function:

Render 1

handleClick → Function A


Render 2

handleClick → Function B
Enter fullscreen mode Exit fullscreen mode

Even though the logic is identical:

Function A ≠ Function B
Enter fullscreen mode Exit fullscreen mode

React thinks props changed.


Using useCallback

Example:

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

  return <Child onClick={handleClick} />;
}
Enter fullscreen mode Exit fullscreen mode

Now:

Render 1

handleClick → Function A


Render 2

handleClick → Function A
Enter fullscreen mode Exit fullscreen mode

The reference remains the same.


Real Example with React.memo and useCallback

Parent:

function TodoApp() {
  const [todos, setTodos] = useState([]);

  const addTodo = useCallback(() => {
    setTodos((prev) => [...prev, "New Task"]);
  }, []);

  return <TodoButton onAdd={addTodo} />;
}
Enter fullscreen mode Exit fullscreen mode

Child:

const TodoButton = React.memo(function TodoButton({ onAdd }) {
  console.log("Button Render");

  return <button onClick={onAdd}>Add Todo</button>;
});

Enter fullscreen mode Exit fullscreen mode

Flow:

Parent Render
       |
       ↓
Create Function
       |
       ↓
useCallback Stores Function
       |
       ↓
Child Receives Same Reference
       |
       ↓
React.memo Prevents Render
Enter fullscreen mode Exit fullscreen mode

useCallback vs useMemo

Many developers confuse these Hooks.

Hook Stores
useMemo Computed values
useCallback Functions

Example:

useMemo

const total = useMemo(()=>{
  return calculateTotal(items);
},[items]);
Enter fullscreen mode Exit fullscreen mode

Stores:

Result Value
Enter fullscreen mode Exit fullscreen mode

useCallback

const save = useCallback(()=>{
  submitForm();
},[]);
Enter fullscreen mode Exit fullscreen mode

Stores:

Function Reference
Enter fullscreen mode Exit fullscreen mode

Real-World Example: Banking Application

Imagine a banking dashboard:

Components:

Dashboard
   |
   |
   ├── AccountSummary
   |
   ├── TransactionList
   |
   └── TransferButton
Enter fullscreen mode Exit fullscreen mode

User changes profile settings.

Without optimization:

Dashboard Re-render
        |
        ↓
All Child Components Render
        |
        ↓
Unnecessary Processing
Enter fullscreen mode Exit fullscreen mode

With optimization:

Dashboard Re-render
        |
        ↓
React.memo Checks Props
        |
        ↓
Unchanged Components Skip Rendering
Enter fullscreen mode Exit fullscreen mode

When Should You Use React.memo?

Good use cases:

✅ Large reusable components
✅ Data tables
✅ Charts
✅ Lists with many items
✅ Expensive UI rendering

Example:

Transaction Table
      |
      ↓
1000 Rows
Enter fullscreen mode Exit fullscreen mode

Memoization can significantly improve performance.


When Should You Avoid React.memo?

Do not use everywhere.

Example:

const SmallText = React.memo(()=>
  <p>Hello</p>
);
Enter fullscreen mode Exit fullscreen mode

For simple components:

  • Comparison cost
  • Extra complexity

may outweigh benefits.


Common Mistakes

Mistake 1: Using useCallback Everywhere

Avoid:

const click = useCallback(()=>{
  console.log("hello");
},[]);
Enter fullscreen mode Exit fullscreen mode

For simple functions, normal functions are enough.


Mistake 2: Forgetting Dependencies

Incorrect:

useCallback(()=>{
  save(user);
},[]);
Enter fullscreen mode Exit fullscreen mode

Problem:

user is missing.

Correct:

useCallback(()=>{
  save(user);
},[user]);
Enter fullscreen mode Exit fullscreen mode

Mistake 3: Expecting useCallback to Improve Everything

useCallback only helps when:

  • Function references matter
  • Child components use React.memo
  • Dependencies are controlled

Performance Optimization Strategy

A practical approach:

Application Slow
        |
        ↓
Measure Performance
        |
        ↓
Find Expensive Components
        |
        ↓
Optimize Specific Areas
        |
        ↓
Use memoization Carefully
Enter fullscreen mode Exit fullscreen mode

Do not optimize before identifying the problem.


React Developer Tools

React provides Developer Tools to analyze:

  • Component renders
  • Props changes
  • Performance issues

Useful features:

  • Profiler
  • Render highlighting
  • Component inspection

Best Practices

  • Optimize only when needed.
  • Use React.memo for expensive components.
  • Use useCallback when passing functions to memoized children.
  • Use useMemo for expensive calculations.
  • Keep components small and focused.
  • Measure before and after optimization.

Key Takeaways

Today, we learned:

✅ React.memo prevents unnecessary component renders.
✅ useCallback memoizes function references.
✅ useMemo stores calculated values.
✅ Memoization is useful for performance optimization.
✅ Optimization should be based on actual performance issues.


Coming Next 🚀

In Day 16, we will explore:

State Management in React – Understanding Context API

We will learn:

  • Why global state is needed
  • Problems with prop drilling
  • Creating Context
  • Provider pattern
  • useContext Hook
  • Real-world authentication example

State management is one of the most important topics for building scalable React applications.

Happy Coding! 🚀

Top comments (0)