How Can Optimizing with React.memo, useMemo & useCallback Improve React Performance?

React.memo useMemo useCallback optimization is key to crafting efficient React applications. Struggling with sluggish performance or unnecessary re-renders? Mastering these tools will solve such issues, enhancing speed and responsiveness. Dive in to transform your coding skills and boost your app’s performance; keep reading to uncover invaluable insights and practical tips.

Why React Components Re-render

In React, components automatically re-render whenever their state or props change. While this behavior ensures the UI stays synchronized with data, it can sometimes cause unnecessary re-renders, which may impact performance in large applications.

Understanding why React components re-render is the first step toward optimizing performance.

1. State Updates

When a component’s state changes, React triggers a re-render of that component.

Example:

import React, { useState } from "react";

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

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

Every time setCount() is called, the Counter component re-renders to update the UI.

2. Parent Component Re-renders

When a parent component re-renders, all of its child components also re-render by default.

Example:

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

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

function Child() {
  console.log("Child rendered");
  return <p>I am a child component</p>;
}

Even though the Child component has no state or props, it still re-renders whenever the Parent component updates.

3. Props Changes

Whenever props change, React re-renders the component receiving those props.

Example:

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

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

function Child({ value }) {
  console.log("Child rendered");
  return <p>Value: {value}</p>;
}

Since value changes every time count updates, the Child component re-renders.

4. Function Recreation on Every Render

In React, functions inside components are recreated on every render. If these functions are passed as props to child components, they can trigger unnecessary re-renders.

Example:

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

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

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

Even if the logic inside handleClick does not change, the function reference changes on each render, causing Child to re-render.

Example Scenario

A common performance issue occurs when parent re-renders cause unnecessary child renders.

Example:

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

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

Flow:

Parent re-render
      ↓
Child component re-render
      ↓
Even when Child data didn't change

To avoid this, React provides optimization tools like:

  • React.memo
  • useMemo
  • useCallback

What is React.memo?

Definition

React.memo is a higher-order component (HOC) used to prevent unnecessary re-renders of functional components.

It works by memoizing the component, meaning React will reuse the previous rendered result if the props have not changed.

Syntax

const MemoizedComponent = React.memo(Component);

Example:

const Child = React.memo(function Child({ name }) {
  console.log("Child rendered");
  return <p>Hello {name}</p>;
});

How React.memo Works

React.memo performs a shallow comparison of props.

If the props remain the same between renders, React skips rendering the component again.

Comparison example:

Previous props → { name: "John" }
Next props     → { name: "John" }

Result → No re-render

But if props change:

Previous props → { name: "John" }
Next props     → { name: "Mike" }

Result → Component re-renders

Example

Before Optimization

function Child({ name }) {
  console.log("Child rendered");
  return <p>Hello {name}</p>;
}

If the parent re-renders, Child also re-renders even if name is unchanged.

After Optimization Using React.memo

const Child = React.memo(function Child({ name }) {
  console.log("Child rendered");
  return <p>Hello {name}</p>;
});

Now the Child component only re-renders when name changes.

When to Use React.memo

Use React.memo when:

  • Components are pure functional components
  • Props rarely change
  • The component renders expensive UI
  • There are many nested child components

Example cases:

  • Data tables
  • Dashboards
  • Charts
  • Large component trees

What is useMemo?

Definition

useMemo is a React hook that memoizes computed values to avoid expensive recalculations during re-renders.

It ensures that a value is recomputed only when its dependencies change.

Syntax

const memoizedValue = useMemo(() => {
  return computeExpensiveValue(data);
}, [data]);

Example Scenario

Imagine filtering a large list of items.

Before Optimization

function ProductList({ products, search }) {
  const filteredProducts = products.filter(product =>
    product.name.includes(search)
  );

  return (
    <ul>
      {filteredProducts.map(p => (
        <li key={p.id}>{p.name}</li>
      ))}
    </ul>
  );
}

Filtering runs every time the component re-renders, even when products hasn’t changed.

After useMemo Optimization

import { useMemo } from "react";

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

  return (
    <ul>
      {filteredProducts.map(p => (
        <li key={p.id}>{p.name}</li>
      ))}
    </ul>
  );
}

Now filtering runs only when products or search changes.

When to Use useMemo

Use useMemo for:

  • Expensive calculations
  • Filtering large datasets
  • Sorting large lists
  • Derived state
  • Data transformations

Example use cases:

  • Analytics dashboards
  • Financial calculations
  • Large product catalogs
  • Data visualization apps

What is useCallback?

Definition

useCallback is a React hook used to memoize functions, ensuring they are not recreated on every render.

This is useful when passing functions to child components, preventing unnecessary re-renders.

Syntax

const memoizedCallback = useCallback(() => {
  doSomething();
}, [dependencies]);

Problem Without useCallback

Example:

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

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

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

Here, handleClick is recreated on every render.

Even if the logic is the same, the function reference changes, causing the Child component to re-render.

Optimized Example Using useCallback

import { useCallback } from "react";

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

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

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

Now the same function reference is reused, preventing unnecessary child renders.

React.memo vs useMemo vs useCallback

FeatureReact.memouseMemouseCallback
PurposePrevent component re-renderMemoize valuesMemoize functions
Works OnComponentsComputed valuesFunctions
ImprovesRendering performanceCalculation performanceProps stability

Combining React.memo, useMemo & useCallback

In real-world React applications, performance optimization often requires combining React.memo, useMemo, and useCallback. Each tool solves a different performance issue:

  • React.memo → Prevents unnecessary component re-renders
  • useMemo → Memoizes expensive computed values
  • useCallback → Memoizes functions passed as props

Together, they help reduce unnecessary calculations and rendering.

Real-World Example

Consider a scenario where:

  • A Parent component manages application state
  • A Child component displays filtered data
  • The application performs expensive data calculations

Parent Component

import React, { useState, useMemo, useCallback } from "react";
import Child from "./Child";

function Parent({ products }) {
  const [search, setSearch] = useState("");

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

  const handleSearchChange = useCallback((e) => {
    setSearch(e.target.value);
  }, []);

  return (
    <div>
      <input
        type="text"
        placeholder="Search products"
        onChange={handleSearchChange}
      />

      <Child products={filteredProducts} />
    </div>
  );
}

export default Parent;

Child Component

import React from "react";

const Child = React.memo(function Child({ products }) {
  console.log("Child rendered");

  return (
    <ul>
      {products.map(product => (
        <li key={product.id}>{product.name}</li>
      ))}
    </ul>
  );
});

export default Child;

How They Work Together

1. useMemo

Prevents recalculating filtered products unless:

  • products changes
  • search changes

2. useCallback

Ensures the same function reference is passed to the input handler.

3. React.memo

Prevents Child component re-render unless its props actually change.

Result:

  • Less computation
  • Fewer re-renders
  • Better performance

Common Mistakes Developers Make (React.memo useMemo useCallback optimization)

While React optimization hooks are powerful, they are often misused.

1. Using Memoization Everywhere

Not every component needs memoization.

Bad practice:

const Component = React.memo(function Component() {
  return <div>Hello</div>;
});

If the component is simple, memoization may add unnecessary overhead.

2. Wrong Dependency Arrays

Incorrect dependency arrays can cause bugs or stale values.

Example mistake:

useMemo(() => calculateTotal(items), []);

If items changes, the value will not update.

Correct version:

useMemo(() => calculateTotal(items), [items]);

3. Over-Optimization

Premature optimization can make code:

  • Harder to maintain
  • Harder to debug
  • More complex than necessary

React optimization should only be applied after identifying performance issues.

4. Not Profiling Performance

Many developers add optimization hooks without measuring performance first.

React provides tools to identify slow components before optimization.

Performance Optimization Best Practices

To build efficient React applications, follow these best practices.

Use React DevTools Profiler

The React DevTools Profiler helps identify:

  • Slow components
  • Frequent re-renders
  • Expensive operations

This helps developers optimize only the components that actually need it.

Memoize Only When Needed

Memoization should be used when:

  • Components render frequently
  • Expensive calculations exist
  • Large lists are processed

Avoid adding memoization to every component.

Use Key Props Correctly

React relies on keys to track list items efficiently.

Bad example:

{items.map((item, index) => (
  <li key={index}>{item.name}</li>
))}

Better approach:

{items.map(item => (
  <li key={item.id}>{item.name}</li>
))}

Using stable keys prevents unnecessary DOM updates.

Avoid Unnecessary State

Sometimes developers store derived values in state, which causes extra renders.

Bad practice:

const [filteredItems, setFilteredItems] = useState([]);

Better approach:

Compute derived values using useMemo instead.

Real-World Example: Optimizing a React Dashboard (React.memo useMemo useCallback optimization)

Modern applications like analytics dashboards often handle large datasets, which can lead to performance problems.

Scenario

Imagine a dashboard that:

  • Displays thousands of records
  • Allows filtering and sorting
  • Passes functions to multiple child components

Without optimization, the dashboard may become slow.

Step 1: Initial Implementation (Unoptimized)

function Dashboard({ data }) {
  const [search, setSearch] = React.useState("");

  const filteredData = data.filter(item =>
    item.name.includes(search)
  );

  return (
    <Table
      data={filteredData}
      onSearch={value => setSearch(value)}
    />
  );
}

Problems:

  • Filtering runs on every render
  • New function passed to child
  • Table re-renders frequently

Step 2: Optimize Expensive Calculations with useMemo

const filteredData = useMemo(() => {
  return data.filter(item =>
    item.name.includes(search)
  );
}, [data, search]);

Now filtering runs only when data or search changes.

Step 3: Stabilize Functions with useCallback

const handleSearch = useCallback((value) => {
  setSearch(value);
}, []);

This ensures the same function reference is reused.

Step 4: Prevent Table Re-renders with React.memo

const Table = React.memo(function Table({ data, onSearch }) {
  console.log("Table rendered");

  return (
    <div>
      {data.map(item => (
        <p key={item.id}>{item.name}</p>
      ))}
    </div>
  );
});

Now the table re-renders only when data changes.

Final Result

After applying all optimizations:

  • Filtering runs less frequently
  • Child components re-render only when necessary
  • Functions maintain stable references
  • UI becomes much faster

This approach is commonly used in:

  • Analytics dashboards
  • Data visualization apps
  • Financial platforms
  • Large e-commerce applications

Optimising with React.memo, useMemo, and useCallback

  1. Facebook’s News Feed Optimization with React.memo: Facebook, the creator of React, uses React.memo extensively. When users scroll through their News Feed, unnecessary re-renders can cause performance issues. By employing React.memo, Facebook optimises component rendering, ensuring only components with changed props re-render.
    
    const NewsFeed = React.memo(function NewsFeed({ posts }) {
      return (
        
    {posts.map(post => ( ))}
    ); });
    This leads to a smoother scrolling experience by reducing the component updates.

  2. Twitter’s Hashtag Recommendations with useMemo: Twitter enhances hashtag suggestion efficiency using useMemo. By memoizing processed data, unnecessary recalculations during component rendering are avoided.
    
    const getRecommendations = (hashtags, trending) => { /* complex logic */ };
    function HashtagPicker({ hashtags, trending }) {
      const recommendations = useMemo(() => getRecommendations(hashtags, trending), [hashtags, trending]);
      return ;
    }
    
    This approach significantly enhances performance, especially for users with many hashtag options.

  3. Netflix’s Auto-Play Control with useCallback: Netflix utilises useCallback to manage auto-play feature efficiency. By memoizing the callback functions used in event listeners, they ensure callbacks don’t unnecessarily trigger component updates.
    
    function AutoPlayToggle({ isEnabled, toggle }) {
      const handleClick = useCallback(() => toggle(!isEnabled), [isEnabled, toggle]);
      return ;
    }
    
    This efficiently manages the updating of autoplay settings with improved resource management.

React.memo useMemo useCallback optimization


  1. What is the main difference between React.memo and useMemo?
    React.memo is used to wrap functional components to prevent unnecessary re-renders, while useMemo is a hook used to memoize expensive calculations in a component to avoid re-computation on every render.
  2. How do useMemo and useCallback differ in their use cases?
    useMemo is for memoizing the result of computations, while useCallback is used to memoize event handler functions to avoid re-creation on every render.
  3. Does React.memo work with class components?
    No, React.memo is specifically designed for functional components; it doesn’t apply to class components.
  4. Can useCallback impact performance negatively?
    Yes, improper usage of useCallback can create unnecessary complexity and actually degrade performance if used to the point where it bloats the codebase unnecessarily.
  5. Are memoization hooks suitable for all React components?
    Not always. Memoization hooks should be used when components re-render frequently and there’s a tangible benefit, otherwise, they might complicate code without real benefit.
  6. How do hooks affect component state management?
    Hooks like useMemo and useCallback don’t manage state; they optimize performance by reducing the number of operations React needs to perform during rendering.
  7. Is it necessary to use a memoization technique for every function?
    No, it isn’t. Utilize memoization only for functions with heavy computation or when components suffer from excessive re-renders impacting performance.

  8. What are some real-world scenarios where useCallback is beneficial?
    Use useCallback in scenarios like memoized components that rely on callback functions, passing functions to deeply nested components, or working with third-party libraries that depend on referential equality.
    const handleClick = useCallback(() => {
    // Logic here
    }, [dependencies]);
  9. Can useMemo and useCallback be used together?
    Yes, they complement each other well, especially in components with complex logic where state and callbacks need to be memoized together for optimal performance.

Discover our AI-powered js online compiler, where you can instantly write, run, and test your code. Our intelligent compiler simplifies your coding experience, offering real-time suggestions and debugging tips, streamlining your journey from coding novice to expert with ease. Explore it today and boost your coding skills!

Conclusion

Completing ‘React.memo useMemo useCallback optimization’ enhances app performance and minimizes unnecessary re-renders. By mastering these optimizations, you’ll refine your coding skills and boost efficiency in React projects. Feel empowered to try it yourself, and for more insights into Java, Python, and more, explore Newtum.

Edited and Compiled by

This article was compiled and edited by @rasikadeshpande, who has over 4 years of experience in writing. She’s passionate about helping beginners understand technical topics in a more interactive way.

About The Author