---
title: "React performance optimization - memoization demystified"
description:
  "Learn how to optimize the performance of React applications using memoization
  techniques."
canonical_url: "https://www.bigbinary.com/blog/react-performance-optimization-memoization-demystified"
markdown_url: "https://www.bigbinary.com/blog/react-performance-optimization-memoization-demystified.md"
---

# React performance optimization - memoization demystified

Learn how to optimize the performance of React applications using memoization
techniques.

- Author: Abhay V Ashokan
- Published: June 13, 2023
- Categories: ReactJS

When it comes to building fast React applications, performance is a top
priority. Luckily, React has clever techniques built in that take care of
performance optimizations automatically. In fact, React does most of the heavy
lifting for you, so you can focus on building your app without worrying too much
about performance tweaks. However, as your React application scales and becomes
more complex, there are opportunities to further enhance its speed and
efficiency.

In this blog, we will focus on how the memoization of components and proper code
splitting help you squeeze the most out of your application. We assume that you
have a high-level understanding of how
[useCallback](https://courses.bigbinaryacademy.com/advanced-react-js/code-optimization/usecallback-hook),
[useMemo](https://courses.bigbinaryacademy.com/advanced-react-js/code-optimization/react-memo),
and
[React.memo](https://courses.bigbinaryacademy.com/advanced-react-js/code-optimization/react-memo)
works. If so, let's jump right in.

### Initial setup

We'll go through the process of building a website that helps teams manage and
discuss customer feedback. The application contains a dashboard where different
categories of feedback are organized. The user can easily navigate between each
category and view all the feedback classified under it. It's important to note
that for the purpose of this blog, we will be focusing on building a dashboard
prototype rather than a fully functional application, incorporating a
significant amount of demo data.

![Customer feedback dashboard](https://www.bigbinary.com/blog/images/images_used_in_blog/2023/react-performance-optimization-memoization-demystified/customer-feedback-dashboard.gif)

The dashboard consists mainly of four components. The `Header` component
displays the category of feedback and also lets you easily navigate to other
categories. The `Category` component displays all the feedback related to the
selected category. On the right side, the `Info` section provides personalized
details about the user who is currently logged in. All these parts are
encapsulated in the `App` component, creating a cohesive dashboard.

**App.jsx**

The `App` component renders the `Header` and `Category` components corresponding
to the selected category. For the sake of demonstration, we store all the dummy
data in the `FEEDBACK_CATEGORIES` variable. By default, the first category is
selected. We shall pass a `DEFAULT_USER` constant to render the `Info`
component.

```jsx
import React, { useState } from "react";

import Category from "./Category";
import { DEFAULT_USER, FEEDBACK_CATEGORIES } from "./constants";
import Header from "./Header";
import Info from "./Info";

const App = () => {
  const [selectedCategoryIndex, setSelectedCategoryIndex] = useState(0);
  const totalCategories = FEEDBACK_CATEGORIES.length;
  const category = FEEDBACK_CATEGORIES[selectedCategoryIndex];

  const gotoNextCategory = () => {
    setSelectedCategoryIndex(index => (index + 1) % totalCategories);
  };

  const gotoPrevCategory = () => {
    setSelectedCategoryIndex(
      index => (index + totalCategories - 1) % totalCategories
    );
  };

  return (
    <div className="flex justify-between">
      <div className="w-full">
        <Header
          gotoNextCategory={gotoNextCategory}
          gotoPrevCategory={gotoPrevCategory}
          title={category.title}
        />
        <Category category={category} />
      </div>
      <Info user={DEFAULT_USER} />
    </div>
  );
};

export default App;
```

**Header.jsx**

The `Header` component renders the feedback title and implements pagination for
seamless navigation between feedback.

```jsx
import React from "react";

const Header = ({ title, gotoNextCategory, gotoPrevCategory }) => (
  <div className="flex justify-between p-4 shadow-sm">
    <h1 className="text-xl font-bold">{title}</h1>
    <div className="space-x-2">
      <button
        className="rounded bg-blue-500 py-1 px-2 text-white"
        onClick={gotoPrevCategory}
      >
        Previous
      </button>
      <button
        className="rounded bg-blue-500 py-1 px-2 text-white"
        onClick={gotoNextCategory}
      >
        Next
      </button>
    </div>
  </div>
);

export default Header;
```

**Category.jsx**

The `Category` component displays a list of feedbacks based on selected
category, facilitating a bird's-eye view of all the feedbacks.

```jsx
import React from "react";

const Category = ({ category }) => (
  <div className="mx-auto my-4 w-full max-w-xl space-y-4">
    {category.feedbacks.map(({ id, user, description }) => (
      <div key={id} className="rounded shadow px-6 py-4 w-full">
        <p className="font-semibold">{user}</p>
        <p className="text-gray-600">{description}</p>
      </div>
    ))}
  </div>
);

export default Category;
```

**Info.jsx**

The `Info` component displays the information of the currently logged-in user.

```jsx
import React from "react";

const Info = ({ user }) => (
  <div className="flex h-screen flex-col bg-gray-100 p-8">
    <p>{user.name}</p>
    <p className="font-semibold text-blue-500">{user.email}</p>
    <button className="text-end mt-auto block text-sm font-semibold text-red-500">
      Log out
    </button>
  </div>
);

export default Info;
```

Here is a
[CodeSandbox link](https://codesandbox.io/s/react-performance-optimization-memoization-demystified-initial-xbu74g?file=/src/App.jsx)
for you to jump right in and try out all the changes yourselves.

### Profiling and optimizing with React profiler

Now let's have a look at what the React Profiler has to say when we click on the
"Next" and "Previous" buttons to navigate between the different feedbacks.

![React profiler for initial code](https://www.bigbinary.com/blog/images/images_used_in_blog/2023/react-performance-optimization-memoization-demystified/react-profiler-initial-code.gif)

Clearly, every component is re-rendered whenever the user navigates to another
feedback. This is not ideal. We can argue that the `Info` component need not be
re-rendered since the user information stays constant across every render. Let
us wrap the default export of the `Info` component with `React.memo` and see how
it improves the performance.

```jsx
export default React.memo(Info);
```

![React profiler for memoized Info component](https://www.bigbinary.com/blog/images/images_used_in_blog/2023/react-performance-optimization-memoization-demystified/react-profiler-memoized-info.gif)

It's now clear that subsequent renders use the cached version of the `Info`
component boosting the overall performance.

Let's now explore how we can improve the performance of the `Header` component
further. There is no point in memoizing the `Header` component itself since the
`title` is updated whenever we navigate to a new category. We can see that the
pagination buttons need not re-render whenever we navigate to a new category.
Hence, it is possible to extract these buttons into their own component and
memoize it to prevent these unnecessary re-renders.

**Header.jsx**

```jsx {3, 9-12}
import React from "react";

import Pagination from "./Pagination";

const Header = ({ title, gotoNextCategory, gotoPrevCategory }) => (
  <div className="flex justify-between p-4 shadow-sm">
    <h1 className="text-xl font-bold">{title}</h1>
    <div className="space-x-2">
      <Pagination
        gotoNextCategory={gotoNextCategory}
        gotoPrevCategory={gotoPrevCategory}
      />
    </div>
  </div>
);

export default Header;
```

**Pagination.jsx**

```jsx
import React from "react";

const Pagination = ({ gotoNextCategory, gotoPrevCategory }) => (
  <>
    <button
      className="rounded bg-blue-500 py-1 px-2 text-white"
      onClick={gotoPrevCategory}
    >
      Previous
    </button>
    <button
      className="rounded bg-blue-500 py-1 px-2 text-white"
      onClick={gotoNextCategory}
    >
      Next
    </button>
  </>
);

export default React.memo(Pagination);
```

This did not solve the problem. In fact, `React.memo` did not prevent any
unnecessary re-renders. By definition, `React.memo` lets you skip re-rendering a
component when its props are unchanged. After close inspection, you will
understand that the references to the `gotoNextCategory` and `gotoPrevCategory`
functions get updated whenever the `App` component re-renders. This causes the
`Pagination` component to re-render as well. Here we should use the
`useCallback` hook to cache the function references before passing them as
props. This would maintain the referential equality of the functions across
renders and let `React.memo` does its magic.

**App.jsx**

```jsx {6-14}
// Rest of the code

const App = () => {
  // Rest of the code

  const gotoNextCategory = useCallback(() => {
    setSelectedCategoryIndex(index => (index + 1) % totalCategories);
  }, []);

  const gotoPrevCategory = useCallback(() => {
    setSelectedCategoryIndex(
      index => (index + totalCategories - 1) % totalCategories
    );
  }, []);

  // Rest of the code
};

export default App;
```

Now, you may use the profiler to verify that the `Pagination` component is not
re-rendered unnecessarily.

![React profiler with memoized pagination](https://www.bigbinary.com/blog/images/images_used_in_blog/2023/react-performance-optimization-memoization-demystified/react-profiler-memoized-pagination.gif)

Let us now introduce a new feature. The users should be able to filter the
feedback in a particular category based on a search term. We shall modify the
`Category` component to incorporate it.

![Feedback search feature](https://www.bigbinary.com/blog/images/images_used_in_blog/2023/react-performance-optimization-memoization-demystified/feedback-search-feature.gif)

**Category.jsx**

```jsx {1, 4, 6-9, 12-19}
import React, { useState } from "react";

const Category = ({ category }) => {
  const [searchTerm, setSearchTerm] = useState("");

  const filteredFeedbacks = category.feedbacks.filter(({ description }) =>
    description.toLowerCase().includes(searchTerm.toLowerCase().trim())
  );

  return (
    <div className="mx-auto my-4 w-full max-w-xl space-y-4">
      <input
        autoFocus
        className="outline-gray-200 w-full border p-2"
        placeholder="Search feedbacks"
        value={searchTerm}
        onChange={e => setSearchTerm(e.target.value)}
      />
      {filteredFeedbacks.map(({ id, user, description }) => (
        <div key={id} className="rounded shadow px-6 py-4 w-full">
          <p className="font-semibold">{user}</p>
          <p className="text-gray-600">{description}</p>
        </div>
      ))}
    </div>
  );
};

export default Category;
```

From the above code, it is very clear that the individual feedback need not be
re-rendered every time the search term is updated. Hence, it would be a good
idea to extract it to a `Card` component and wrap it with React.memo.

**Category.jsx**

```jsx {3, 22}
import React, { useState } from "react";

import Card from "./Card";

const Category = ({ category }) => {
  const [searchTerm, setSearchTerm] = useState("");

  const filteredFeedbacks = category.feedbacks.filter(({ description }) =>
    description.toLowerCase().includes(searchTerm.toLowerCase().trim())
  );

  return (
    <div className="mx-auto my-4 w-full max-w-xl space-y-4">
      <input
        autoFocus
        className="outline-gray-200 w-full border p-2"
        placeholder="Search feedbacks"
        value={searchTerm}
        onChange={e => setSearchTerm(e.target.value)}
      />
      {filteredFeedbacks.map(({ id, user, description }) => (
        <Card key={id} user={user} description={description} />
      ))}
    </div>
  );
};

export default Category;
```

**Card.jsx**

```jsx
import React from "react";

const Card = ({ user, description }) => (
  <div className="w-full rounded px-6 py-4 shadow">
    <p className="font-semibold">{user}</p>
    <p className="text-gray-600">{description}</p>
  </div>
);

export default React.memo(Card);
```

If the number of feedback is too large, the calculation of `filteredFeedbacks`
will become expensive. We need to traverse through all the comments one by one
and then perform a custom search logic on each comment object. We can use the
`useMemo` hook to cache the results, preventing the same computation across
renders boosts the performance further.

Let us verify the React profiler one last time. Clearly, only the `Category`
component re-renders with the new changes, taking the rest of the results from
the cache.

![React profiler useMemo and memoized Card](https://www.bigbinary.com/blog/images/images_used_in_blog/2023/react-performance-optimization-memoization-demystified/react-profiler-search-feature.gif)

To enhance the overall user experience, it's important to reset the search term
whenever users navigate to a new category. This can be easily achieved by
passing a `key` prop to the `Category` component. React will maintain separate
component trees for each category, ensuring that the search functionality starts
anew in each category.

```jsx {14}
// Rest of the code

const App = () => {
  // Rest of the code

  return (
    <div className="flex justify-between">
      <div className="w-full">
        <Header
          title={feedback.title}
          gotoNextCategory={gotoNextCategory}
          gotoPrevCategory={gotoPrevCategory}
        />
        <Category key={category.id} category={category} />
      </div>
      <Info user={DEFAULT_USER} />
    </div>
  );
};

export default App;
```

### Wrapping up

By breaking down components and utilizing memoization, we have improved the
performance of our app significantly. The React Profiler has been instrumental
in identifying areas for optimization and validating the effectiveness of our
enhancements. With these techniques, you can now build faster and more
responsive React applications. Apply these learnings to your projects and
elevate your React development skills.

Here is a
[CodeSandbox link](https://codesandbox.io/s/react-performance-optimization-memmoization-demystified-xef0q9)
of the optimized website for you to play with.

## Links

- [Human page](https://www.bigbinary.com/blog/react-performance-optimization-memoization-demystified)
