Skip to content

React-Virtualized: Why, When and How you should use it

Cyril Gaunet5 min read

Why

What is a Virtual DOM ?

The virtual DOM (VDOM) is a programming concept where an ideal, or “virtual”, representation of a UI is kept in memory.
Then, it is synced with the “real” DOM by a library such as ReactDOM. This process is called reconciliation.

Performance and windowing

You might know that React uses this virtual DOM. Thus, it is only when React renders elements that the user will have them into his/her HTML DOM.

Sometimes you might want to display a lot of html elements, like for grids, lists, calendars, dropdowns etc and the user will often complain about performance.

fuyong-hua-658447-unsplash

Hence, a good way to display a lot of information is to ‘window’ it. The idea is to create only elements the user can see. An example is the Kindle vs Book. While the book is a heavy object because it ‘renders’ all the pages, the Kindle only display what the user can see.

React-Virtualized

That is how Brian Vaughn came up with the idea of creating React-Virtualized.

It is an open-source library which provides you many components in order to window some of your application List, Grid etc

As a developer, you do not want to reinvent the wheel. React-virtualized is a stable and maintained library. Its community is large and as it is open-source, many modules and extensions are already available in order to window a maximum of elements.

Furthermore, it offers lots of functionalities and customization that you would not even think about.

We will discuss about it later, but before, let’s see when to use React-virtualized.

When

When thinking about performance, lots of actions can be taken, but React official website already got a complete article to be read. In consequence, if you face a performance problem, be sure you have already done all of these before to start to window your application (but stay pragmatic).

How

Getting into it

jesse-orrico-184803-unsplash

Ok, now that you’re convinced, let’s go throught the real part.

You can begin by following instructions for installing the right package via npm and look at simple examples here : React virtualized github. However, I’m going to show you a complex example so you can use React-Virtualized in an advanced way.

React-Virtualized 101

To render a windowed list, no need for digging one hour a complex documentation, React-Virtualized is very simple to use.

Firstly, you use the List component from the library, then, the few important props are the next one:

The example

import React from 'react';
import { List } from 'react-virtualized';

// List data as an array of strings
const list = [
    'Easy windowing1',
    'Easy windowing2',
// And so on...
];

function rowRenderer({
    key, // Unique key within array of rows
    index, // Index of row within collection
    isScrolling, // Used for performance
    isVisible, // Used for performancee
    style, // Style object to be applied to row (to position it)
}) {
    return (
        <div key={key} style={style}>
            {list[index]}
        </div>
    );
}

// Render your list
const ListExample = () => (
    <List width={300} height={300} rowCount={list.length} rowHeight={20} rowRenderer={rowRenderer} />
);

Click here to see a demo

A more complex virtualized list:

Display a virtualized list might be easy, but you might have a complicated behaviour to implement.

In this advanced example, we will:

This advanced example goes through 4 steps:

The example

Let’s look first at how we render the list:

import {
    AutoSizer,
    List,
    CellMeasurer,
    CellMeasurerCache,
} from 'react-virtualized';
...
<AutoSizer>
    {({ height, width }) => (
        <List
            width={width}
            height={height}
            rowGetter={({ index }) => rows[index]}
            rowCount={1000}
            rowHeight={40}
            rowRenderer={this.rowRenderer}
            headerHeight={20}
        />
    )}
</AutoSizer>

It is very simple:

How it works :

First, you instantiate a new CellMeasurerCache that will contain all the calculated heights :

constructor(props) {
    super(props);
    this.cache = new CellMeasurerCache({ //Define a CellMeasurerCache --> Put the height and width you think are the best
        defaultHeight: 80,
        minHeight: 50,
        fixedWidth: true,
    });
}

Then, you use the CellMeasurer in the rowRenderer method:

rowRenderer = ({
    key, // Unique key within array of rows
    index, // Index of row within collection
    parent,
    isScrolling, // The List is currently being scrolled --> Important if you need some perf adjustment
    isVisible, // This row is visible within the List (eg it is not an overscanned row)
    style, // Style object to be applied to row (to position it)
}) => (
    <CellMeasurer
        cache={this.cache}
        columnIndex={0}
        key={key}
        parent={parent}
        rowIndex={index}
    >
        <div
            className="Row"
            key={key}
            style={{
                ...style,
                display: 'flex',
            }}
        >
        <span style={{ width: 400 }}>{rows[index].name}</span>
        <span style={{ width: 100 }}>{rows[index].age}</span>
        </div>
    </CellMeasurer>
);

Pitfall:

Finally, we obtain a nice windowed list, ready to be deployed and used…

Unless your application contain filters or some data added dynamically.

Actually, when I implemented this, after using some filters, some blank spaces were staying in the list.

It is a performance consideration due to the fact we use a cache, but it is a good compromise unless you have many rows and many columns in a Grid (as we display a list, we only have 1 column).

Consequently, I managed to fix this issue by clearing the cache every time my list had its data reloaded:

componentWillReceiveProps() { //Really important !!
    this.cache.clearAll(); //Clear the cache if row heights are recompute to be sure there are no "blank spaces" (some row are erased)
    this.virtualizedList && this.virtualizedList.recomputeRowHeights(); //We need to recompute the heights
}

A big thanks to Brian Vaughn for this amazing library