How To Improve Rendering Performance in a 1,000-Item React List

mohammadfaisal

Mohammad Faisal

Posted on July 30, 2023

How To Improve Rendering Performance in a 1,000-Item React List

To read more articles like this, visit my blog

Imagine you are building some kind of application with React that requires you to show a long list of items. Say, 1,000 items. What are your options? Just render the whole thing at once?

Today, my goal is to show you the problem so that you understand what we are solving and then present a solution.

Let's get started!

Create the Project

At this point, all of you should know how to create a new React application from the command line using create-react-app. Just go to your terminal and run the following command to get going with a brand new React application:



npx create-react-app long-list-app


Enter fullscreen mode Exit fullscreen mode

Let’s Render 1,000 Items

OK, let me first show you the problem. Have a look at the following component:



import React, {useEffect, useState} from 'react';

const generateNames = (count) => {
    const temp = [];
    for(let i=0;i<=count;i++) temp.push(`Test Name- ${i}`)
    return temp;
}

export const LongList = () => {
    const [names , setNames ] = useState([])

    useEffect(() => {
        setNames(generateNames(1000));
    },[])

    return <> {names.map(name => <ListItem name={name}/>)} </>
}

const ListItem = ({name}) => {
    console.log(`rendered ${name}`)
    return <div> Name is: {name} </div>
}


Enter fullscreen mode Exit fullscreen mode

In this component, we are generating 1,000 names and rendering them inside a list. Now let’s have a look in the browser:

Inefficient rendering

Although our window is capable of showing six items, from the console, we can see that 1,000 items are being rendered in the background!

This inefficient rendering can make your application really slow.

Now Show Me the Code!

We will use an awesome library named react-virtualized. The concept is really simple and elegant: Don’t render stuff you don’t see on the screen!

First, install the dependency:



yarn add react-virtualized


Enter fullscreen mode Exit fullscreen mode

What this library does is export some wrapper components to wrap around your list. You provide the height, width, and a function to render, and the library handles the rest.

Let's update our code to use the List component exported from the library:



import React, {useEffect, useState} from 'react';
import {List} from 'react-virtualized';

export const LongList = () => {

    // SAME AS BEFORE

    const renderRow = ({ index, key, style }) => {
        return <ListItem style={style} key={key} name={names[index]}/>
    }

    return <List
        width={800}
        height={900}
        rowHeight={30}
        rowRenderer={renderRow}
        rowCount={names.length}
    />
}


Enter fullscreen mode Exit fullscreen mode

We have changed the rendering process with the List component.

Notice one thing here: We passed some extra props. width, height, rowHeight, and rowCount are self-explanatory, while rowRender is a render function for each row item.

Now let's see the result:

After optimization

Now we can see that there are 39 items on the screen and exactly 39 items are rendered now. No unnecessary rendering!

Variable-Sized Screen

You have likely noticed that we are giving the height and width of the container as a constant. But what if the screen of the users is of a different size. You just can’t say, “But it works on my machine!”

react-virtualized has already solved this problem. It has another component named AutoSizer that helps to find the height and width of the container of the list dynamically. It follows a render-props pattern.

Let’s update our code like so:



import React, {useEffect, useState} from 'react';
import {List , AutoSizer} from 'react-virtualized';

export const LongList = () => {

    // SAME AS BEFORE

    return <div style={{height:"100vh"}}>
        <AutoSizer>
            {({ width, height }) => {
                return <List
                    width={width}
                    height={height}
                    rowHeight={30}
                    rowRenderer={renderRow}
                    rowCount={names.length}
                    overscanRowCount={3} />
            }}
        </AutoSizer>
    </div>
}


Enter fullscreen mode Exit fullscreen mode

Now the height and width of the screen are automatically updated based on the user's screen size.

Variable-Sized Row

So we solved the height and width issue of the container. But what about the individual row items? What if their size varies based on the content?

react-virtualized has a solution for that too. You need to use another component named CellMeasurer for that. We will use another component named CellMeasurerCache for caching the size of the component.

Now look at the modified rendering process:



import React, {useEffect, useState} from 'react';
import {List, AutoSizer, CellMeasurerCache, CellMeasurer} from 'react-virtualized';

export const LongList = () => {

<span class="c1">// SAME AS BEFORE</span>

<span class="kd">const</span> <span class="nx">cache</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">CellMeasurerCache</span><span class="p">({</span>
    <span class="na">defaultWidth</span><span class="p">:</span> <span class="mi">500</span><span class="p">,</span>
    <span class="na">defaultHeight</span><span class="p">:</span> <span class="mi">900</span>
<span class="p">});</span>

<span class="kd">const</span> <span class="nx">renderRow</span> <span class="o">=</span> <span class="p">({</span> <span class="nx">index</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">style</span> <span class="p">,</span> <span class="nx">parent</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="k">return</span> <span class="o">&lt;</span><span class="nx">CellMeasurer</span>
        <span class="nx">key</span><span class="o">=</span><span class="p">{</span><span class="nx">key</span><span class="p">}</span>
        <span class="nx">cache</span><span class="o">=</span><span class="p">{</span><span class="nx">cache</span><span class="p">}</span>
        <span class="nx">parent</span><span class="o">=</span><span class="p">{</span><span class="nx">parent</span><span class="p">}</span>
        <span class="nx">columnIndex</span><span class="o">=</span><span class="p">{</span><span class="mi">0</span><span class="p">}</span>
        <span class="nx">rowIndex</span><span class="o">=</span><span class="p">{</span><span class="nx">index</span><span class="p">}</span><span class="o">&gt;</span>

        <span class="o">&lt;</span><span class="nx">div</span> <span class="nx">style</span><span class="o">=</span><span class="p">{</span><span class="nx">style</span><span class="p">}</span><span class="o">&gt;</span>
          <span class="nx">Name</span> <span class="na">is</span><span class="p">:</span> <span class="p">{</span><span class="nx">names</span><span class="p">[</span><span class="nx">index</span><span class="p">]}</span>
        <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
Enter fullscreen mode Exit fullscreen mode


</CellMeasurer>
}

<span class="k">return</span> <span class="o">&lt;</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="p">{</span><span class="dl">'</span><span class="s1">list</span><span class="dl">'</span><span class="p">}</span><span class="o">&gt;</span>
    <span class="o">&lt;</span><span class="nx">AutoSizer</span><span class="o">&gt;</span>
        <span class="p">{({</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span>
            <span class="k">return</span> <span class="o">&lt;</span><span class="nx">List</span>
                <span class="nx">width</span><span class="o">=</span><span class="p">{</span><span class="nx">width</span><span class="p">}</span>
                <span class="nx">height</span><span class="o">=</span><span class="p">{</span><span class="mi">200</span><span class="p">}</span>
                <span class="nx">rowRenderer</span><span class="o">=</span><span class="p">{</span><span class="nx">renderRow</span><span class="p">}</span>
                <span class="nx">rowCount</span><span class="o">=</span><span class="p">{</span><span class="nx">names</span><span class="p">.</span><span class="nx">length</span><span class="p">}</span>
                <span class="nx">rowHeight</span><span class="o">=</span><span class="p">{</span><span class="nx">cache</span><span class="p">.</span><span class="nx">rowHeight</span><span class="p">}</span>
                <span class="nx">deferredMeasurementCache</span><span class="o">=</span><span class="p">{</span><span class="nx">cache</span><span class="p">}</span>
            <span class="sr">/</span><span class="err">&gt;
Enter fullscreen mode Exit fullscreen mode

}}
</AutoSizer>
</div>

}

Enter fullscreen mode Exit fullscreen mode




Conclusion

So there you have it. I just scratched the surface of what’s possible with react-virtualized and how it can help us to significantly improve low-performing React applications.

Have a great day!

Get in touch with me via LinkedIn or Personal website.

💖 💪 🙅 🚩
mohammadfaisal
Mohammad Faisal

Posted on July 30, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related