What I learned from building my own virtualized list library for React
Nishan Bajracharya
Posted on August 21, 2018
A project I’m working on has a contact list that needs to show 5000 contacts on average. That means 5000 complex DOM elements with lots of nested components need to be rendered on the screen. We knew when we started building this that rendering so many of these elements would be a very taxing operation on the DOM. And React, ever so dilligent, will absolutely render all the DOM elements if we were to map them into our view even if it kills our rendering performance.
{
LIST_WITH_5000_ITEMS.map(item => <ComplexListItem {...item} />)
}
So we opted for a better approach than this. Our first approach was to use an infinite scroll component that would load 250 contacts at a time and after reaching to the end of the list would load another 250 contacts. This made the initial render performance better but would start to get considerably slower as you scroll to the end. This did not fix the original issue at hand.
Our second approach was to use something called a virtualized list. This fixed the majority of our underlying rendering and performance issues related to the list DOM and scrolling and we could finally focus on optimizing the non-DOM related issues with the contact list logic, like a heavy fetch API request.
What is a virtualized list?
A virtualized list is a technique where a large list of items in a scrollable view is virtually rendered to only show items that are visible within the the scrollable view window. That may sound like a handful but it basically means a virtualized list only renders items that are visible on the screen.
Why the need for this?
Consider an application that deals with a large list of items, say 10000 rows in a list. What is the best and most performant way to render this list? Some would say a paginated list where rows are unmounted when the page is changed. But what about infinite scroll views, ones that add rows to the bottom of the current view once we scroll towards the bottom. Each of the rows are rendered and more rows get rendered as we scroll below.
We could have a view window that can only show 20 items at a time but we are rendering tens of thousands of DOM elements. This is very ineffective and causes a lot of sluggish scrolling and unresponsive lists. This is no good.
Ideally we would only render the items that are visible. The items outside of the view window don’t need the rendering power. Only when they come into the visible window do they need to be rendered. This is where virtualized list comes into view.
Is this a new idea?
No. In fact, the idea of virtualized lists has been available for quite a while now. Android development has had the Recycler View since 2014, React Native provides VirtualizedList component out of the box and for React developers, we have a highly extensible library called react-virtualized. Each of these libraries and features may have different implementations, but they all try to fix the same issue.
What came next?
I became interested in how a virtualized list works behind the scenes. A virtualized list doesn’t have any scrolling artifacts that you might expect considering it is rendering new items that come into view and un-rendering items that get out of the view on the fly. It works basically the same as a non virtualized list, showing the scrollbar on the side with the same functionality when dragging the scroll bar.
So how does it imitate these behaviors and how is it keeping track of what elements are on the view? These were the burning questions running through my head when I started looking into the inner workings of a virtualized list. I had read a blog about virtualized list I had received from a friend a while back and all I remember from that is that instead of rendering a list as you would normally do, you use position: absolute on the elements and keep track of the scroll position of the list.
So I started to work on my own implementation of a virtualized list in the form of a React library that I brilliantly called react-virtualized-listview . Since I had already worked with react-virtualized , I was heavily inspired by that library’s API. And I also wanted something that a lot more simpler than all the various features that react-virtualized provided. Don’t get me wrong, react-virtualized is definitely among the best libraries of available for React, but it was a little daunting to start with because of all the various features it provides. I wanted something that was much easier to use and in the process of building the library, I would understand how virtualized list worked.
An example of how this would work in code:
const data = [1, 2, 3, 4, 5];
<List
source={data}
rowHeight={40}
renderItem={({ index, style }) => (
<div key={index} style={style}>
Hello {index}
</div>
)}
/>
So how does it work?
Let’s take a list with a 1000rows. Say each row is 20pxin height. So we would have a list with a height of 20000px. This is where the virtualized list starts. It creates a DOM element that matches the width dimension of its parent visible window and a height that equals the total item count multiplied by the height of the item (20000px here). This is so that the position of the scrollbar perfectly imitates the behavior of a non-virtualized list. So scrolling the mouse wheel on the list and dragging the scrollbar both work as intended. The DOM element is empty at this point.
Next we keep track of the scroll position within the list. This is the critical part of the virtualization. The scroll position determines which index we’re at in the list. This index coupled with the height of the visible window determines the indices that are visible and need to be rendered. The list item to be rendered is given a position: absolute style and a top value calculated using the index of the item and the row height of the item. So we only render the items that match the calculated indices.
One additional trick we use to imitate a non-virtualized list is the overscan. We render a small number of non-visible items above and below the visible window so that it appears like other items exist when scrolling, as opposed to just popping into view when it enters the visible window.
A codesandbox example of the virtualized list in action
React virtualized listview on Codesandbox
More on virtualized lists.
Posted on August 21, 2018
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.