Poorly-optimized React components, an example

Aron Schüler Published
Updated on


Sandbox: https://codesandbox.io/s/poorly-optimized-list-9qj0yq?file=/src/App.tsx

Hi and welcome to my blog post on optimizing the performance of a React component. In this post, I will be analyzing an example poorly-optimized React function component that renders a list of items. I will be using the React performance profiler and other tools to identify the performance issues in the component, and then I will show you how to optimize the component to improve its performance. I will be using code snippets and examples throughout this post, and I have also provided a code sandbox for each step, so you can follow along. By the end of this post, you should have a better understanding of how to optimize the performance of your React components. Let’s get started!

Analyzing the performance of the component

To analyze a React application, the best tool is the Profiler contained in the React Dev Tools. You should probably have these installed already. If not, you can have a look at the installation guide in the React Docs here.

Before jumping into the analysis of our example component, familiarize yourself a bit with it in the code sandbox here. The example app has a button which will either assign a new value shown above the button or generate new data for the list below the button. It also counts renderings and explains which value was changed how often. Go ahead and click on the Get new data button to trigger either a couple of times.

The important code here is the MyList component, which receives an array of values and maps these to list items in its rendering:

export const MyList = ({
  items,
}: {
  items: { id: number; name: string }[];
}) => (
  <div>
    <p>
      This list contains <b>{items.length}</b> items.
    </p>
    <ul>
      {items.map((item) => (
        <li key={item.id}>{obfuscate(item.name)}</li>
      ))}
    </ul>
  </div>
);

Below, I used the React Dev Tools Profiler to generate a report of the App components performance, which contains the poorly-optimized MyList component. I will use this report to see how long each component takes to render, and we will see that the MyList component takes the most time of this rendering process.

Here is a screenshot of the React Dev Tools profiler, underlining the slow rendering of the App component and the MyList component within:

Poorly Optimized List As you can see from the screenshot, the MyList component takes a significant amount of time to render. The output is also showing that it is rendering unnecessarily often. You can also see this in the output of the code sandbox app’s texts, although not in the React Dev Tools, as they are not available through Code Sandbox.

If we have a look at the code, we can also see where this behavior is coming from. The App component has no idea when and how items changed, its always a different array in JavaScript. So, instead of only re-rendering when items changed, the children component re-renders every time the parent re-renders. Which is on every button click! As the MyList component uses the really heavy obfuscate() function in its rendering call, it also takes a lot of time to render every li element for the array and is unnecessary.

The re-rendering and amount of time spent on calculating the items in the MyList component is a clear performance issue, and we will discuss how to address this issue in the next section.

Optimizing the component

Optimizing the re-rendering

We want to tackle the re-rendering of MyList first. For a long time, in React’s class-based components, this was done by implement a shouldComponentUpdate lifecycle method. Nowadays, this cannot be used anymore in functional components. These components can be wrapped in React’s memo function:

export const MyList = memo<{
  items: { id: number; name: string }[];
}>(({ items }) => (
  <div>
    <p>
      This list contains <b>{items.length}</b> items.
    </p>
    <ul>
      {items.map((item) => (
        <li key={item.id}>{obfuscate(item.name)}</li>
      ))}
    </ul>
  </div>
));

Now, this is much better, as MyList will now only re-render if we actually have a different value for items. This is achieved by React caching the value of items internally. This cache is only thrown away on really special occasions. In your local development server, this will also be you changing the file of a memo-ized component, so don’t wonder there!

But what if the items list changed, but only in e.g. the order of items given in? The really heavy obfuscate() method will be called anyways. Imagine this is an API call, we could save a lot of execution time here as well by somehow caching the values of the mapping as well!


Related Posts

Find posts on similar topics:


Comments