Virtuar Rendering with React

React virtual rendering

Ever wonder how to render a huge number of records in a react component and not killing your browser. For example, rendering a list with thousands of items or a Datagrid with a high density of columns and rows.

One way of solving this problem is to use a technique called "virtual rendering".The basic idea of "VR" is to only render what the user sees, keeping the number of rendered objects to the minimum.

A typical use of "VR" with React will be the implementation of a list component that contains thousands of elements. In this post, I going to show how easy is to implement a React Component that uses the "virtual render" technique. Here is the complete example in jsfiddle.

To begin the first thing that we need is a viewPort area. This area is the one that is going to be responsible to contain the visible items of the list and has a scrollbar that enables the user to navigate through all the items.

The code for this example can be found at Github.

I have also made a react timeline component using the same technic, the repo link is here or can be installed using npm install react-gantt-timeline.

To accomplish this in HTML we are going to create a div called "viewport" and inside we are going to add another div which height is going to be h=height of a row * number of rows. So now the viewport div is going to be able to scroll with the right scroll length, the last thing to do is add to our the viewport css the property overflow-y: scroll.

Virtuar Rendering with React

So lets put this together in a react component and add a simple class for render rows that we are going to call "Item". The code and the css will look like this:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Item extends React.Component{ constructor(props){ super(props);} render(){ return ( <div className="item" style={{top:this.props.top}}> {this.props.label} </div>) } } class Vlist extends React.Component{ constructor(props){ super(props); } render(){ return ( <div ref="viewPort" className="viewPort"> <div className="itemContainer"> </div> </div>) } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 viewPort { position: relative; width: 510px; height: 300px; border: solid 1px; overflow-y: scroll; } .itemContainer { position: absolute; width: 500px; background-color: azure; } .item { position: absolute; background-color: beige; border: solid 1px; width: 500px; text-align:center; }

So now let's create some data and initialize our component state. We are going to create some important variables: 1. The height of a row (itemheight)that we are going to pass as a property 2. With itemheight we can calculate the total height of the itemcontainer div (h=itemheight * numRows) and we are going to create a variable call (containerStyle) to use as an inline style. 3.The number of visible items in the viewPort (numVisibleItems)=viewPort.height/itemheight 4. We are going to initialize the state that will contain the index of the rows to render.

Now that we have all set up we also can create a renderRows method that using start and end can render the visible rows.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 const data = [] function createData(){ for (let i=0;i<10000;i++){ data.push({name:"Row"+i); } } createData(); //Item class to render each of the list rows class Item extends React.Component{ constructor(props){ super(props); } //Adding Style for dynamic rowheight render(){ return ( <div className="item" style={{top:this.props.top,height:this.props.itemheight}}> {this.props.label} </div>) } } class Vlist extends React.Component{ constructor(props){ super(props); //Calculating the number of visible items this.numVisibleItems=Math.trunc(300 / this.props.itemheight); //Initialising index of visible items this.state={ start:0, end:this.numVisibleItems } //Initialise style for the itemContainer this.containerStyle={height:data.length * this.props.itemheight} } //Rendering visble Rows renderRows(){ let result=[]; for (let i=this.state.start;i<this.state.end+1;i++){ let item=data[i]; result.push(<Item key={i} label={item.name} top={i*this.props.itemheight} itemheight={this.props.itemheight} />); } return result; } render(){ return ( <div ref="viewPort" className="viewPort" > <div className="itemContainer" style={this.containerStyle}> {this.renderRows()} </div> </div>) } }

The renderRow method is where all the magic happens, here we only render the visible elements and we position each item to his right top matching with the position of the scroll bar of the viewport div.

That is cool now we have a list that can display the first n visible items with a scrollbar. The last thing to do is to add a listener to the scrollbar so as it moves we can change the state properties start and end index. Changing the state will trigger an update and we will see the right rows rendering given a new scroll position. New items will be created and the not visible items will be destroyed.

This diagram shows what is happening as we scroll:

Virtuar Rendering with React

Now we can take a look at the end result. The code is super fast with almost no impact in the CPU when scrolling.

Author
Angela Jane

Duis a enim vel mauris ultrices. Nullam aliquet velit ac velit tempus in semper an neque auctor. Aenean ligula mi, auctor sed tempus.Duis a enim vel mauris ultrices. Nullam aliquet velit ac velit tempus in semper an neque auctor. Aenean ligula mi, auctor sed tempus.

Try Kubernetes Studio

Test our Latest app. It is called K8 Studio and is a cross-platform IDE to manage Kubernetes Clusters visually.

Download for Mac Download for Windows
K8 Studio