Irshadi Bagasputro
05 March 2023 · 5 min read
There may be times where we need to display a large list or large table that contains many columns or many rows – sometimes, even both. Loading every single item on such a list can affect performance significantly. Enter virtualized list —or also know as “windowing”, which was a concept of only rendering what is visible to users. This concepts solve the performance problems of rendering large list or table.
virtualized-list-concept.gif
"Virtualizing" a list of items involves maintaining a window and moving that window around your list. The number of elements that are rendered at first is a very small subset of the entire list and the "window" of visible content moves when the user continues to scroll. Let’s put it this way: Imagine you have 1000 list of elements, rather than rendering 1000 of elements — which can affects performance, because on slower initial rendering or scroll performance. Instead, You only render 5 elements that visible to the user. By implementing virtualized list, it would benefit the low-end devices. You can display more items as the user scrolls, replacing the previous list element with the new one.
There’s several library out there that provides this function. But my top two is by Brian Vaughn, react-virtualized
and react-window
. Personally, I would prefer react-window
over the other just because it’s much smaller and faster. Here’s the comparison:
react-window_bundlesize.png
react-virtualized_bundlesize.png
The API’s for both package are relatively similar, but in this case I will use react-window
.
list-virtualization.png
Lists render a windowed list (row) of elements meaning that only the visible rows are displayed to users (e.g FixedSizeList, VariableSizeList). Lists use a Grid (internally) to render rows, relaying props to that inner Grid.
There’s 99% chance that you already know how to render list data in React. But for a moment let’s assume that you don’t. Imagine we want to render a list of people, in a scroll-able div
container. Given a set of data that looks like this:
1
const people = [2
{3
name: "Aditya Kurnia Harapan",4
age: 275
},6
{7
name: "Azhari Rizkita Priyono",8
age: 249
},10
//...some other people here11
// and then comes my name12
{13
name: "Irshadi Bagasputro",14
age: 2715
}16
// ...and many more17
];
In a traditional—React way, we need to do something like this, basically we iterate the whole array/list with a .map
function. And it will returns the JSX itselfs.
1
const PeopleList = () => {2
return (3
<div>4
{people.map(({ name }) => (5
<Item>{name}</Item>6
))}7
</div>8
);9
};
Usually, there won’t be a problem using this traditional approach, but here’s the interesting part. Image the people
array on the example above, contains a billion of people. What react did is it will iterate, through every item on the list and it will paint it to the DOM. This is painful and expensive things that react can do. The longer the list, the more of user’s device performance used.
One way to prevent this is by using Virtualization Windowed List, instead of iterating through every item on the people array, it will only render several item inside people array. Virtualization achieve this by lazily paint the item to the DOM as user’s scroll through the scroll-able container. With the same set of data, now let’s refactor those code using react-virtualized
.
1
import { List } from "react-virtualized";2
3
const PeopleList = () => {4
const ref = React.useRef();5
6
// More: https://github.com/bvaughn/react-virtualized/blob/master/docs/List.md#rowrenderer7
const rowRenderer = ({ index }) => <Item>{people[index].name}</Item>;8
9
return (10
<List11
ref={ref}12
height={600}13
width={300}14
rowCount={people.length}15
rowHeight={40}16
rowRenderer={rowRenderer}17
overscanRowCount={1}18
/>19
);20
};
Let's dive into the code snippet above, and at first glance, we notice that the only missing piece is the looping process, which is expertly handled by the <List />
component. However, there are a few other mandatory props that we must address, including height
, width
, rowCount
, rowHeight
, and rowRenderer
.
Starting with the rowRenderer
prop, it plays a crucial role in rendering a single row or item, determined by its index in the list. This specialized function allows the item to be virtualized intelligently based on the window or box scanner, optimizing performance by rendering only the visible items on the screen.
The significance of height
and width
cannot be overlooked; they define the dimensions of the virtualized window that contains multiple rows. By setting appropriate height
and width
, we establish the boundaries within which rows will be efficiently virtualized, ensuring a smooth and seamless user experience, regardless of the size of the underlying data.
rowHeight
is another vital prop, which can be either a function or a number. It comes into play during the scanning process, helping to determine the size of rows within the virtualized window. This information is crucial for the intelligent rendering of rows as the user scrolls through the list.
Lastly, let's talk about the rowCount
prop. It holds paramount importance in predicting and determining the total height of the list. The calculated total height is derived from the simple formula rowCount * rowHeight
, and this value aids in detecting the scroll.x
position based on the window scroller.
By leveraging these essential props in tandem with the <List />
component, we harness the power of virtualization, allowing us to efficiently handle and display large datasets while maintaining an optimal user experience. The combination of rowRenderer
, height
, width
, rowHeight
, and rowCount
culminates in a responsive and high-performing list that dynamically adapts to user interactions.
grid-virtualization.png
The next time, I will give an example for Virtualized Grid.
In summary, virtualized lists are a powerful optimization technique that improves the rendering performance of long lists in web and mobile applications by rendering only the visible items on the screen, dynamically adjusting the list content as the user scrolls. This results in a smoother user experience, especially when dealing with large datasets.