November 26, 2020

Implementing virtual scroll using react

In the era of web application, You might need to deal with a huge amount of data and that data need to be rendered on DOM.

In web application, DOM is a very sensitive matter you need to deal with it very carefully because else your application will be laggy or slow.

For eg. you have data of 100,000 rows. Its huge amount of data and rendering total data, It’s going to be a very expensive operation

So, here virtual scroll concept comes for rescue.

Virtual scroll means we will just renders the data which is visible in viewport and other will render as user scroll and comes into the viewport and data which is not in viewport will get remove from DOM.

without wasting time let’s implement virtual scroll using react

Find Github and Demo here

Contents:

  • What we will going to do?
  • Setting up a react project
  • creating virtual scroll component
  • performance optimization

Please subscribe for web development tutorials

What we will going to do?

Virtual scroll using react

As you can see in the above diagram, only rows into the viewport section is visible to the end-user, so we will going to rendered that elements into the DOM with some buffered rows as you can see into the diagram, Because If user scroll we can show already rendered elements until we calculate the visible rows to be shown

we will be having scroll event onto the viewport, as user scroll we will run scroll function and we will calculate next visible rows to be shown

Setting up a react project

you can create initial setup for react using below command

npx create-react-app react-virtual-scroll

create-react-app is a react utility which helps you to setup react project, want to know more in detail then please go through this post

Creating a virtual scroll component

virtual scroll component is responsible for showing only data which is visible into the viewport and and adding and removing element from the dom when we scroll

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

const VirtualScroll = (props) => {
  const totalHeight = +props.rowHeight * +props.totalElements + 'px';
  const [scrollTop, setScrollTop] = useState(0);

  useEffect(() => {
    scrollEle.current.addEventListener("scroll", scroll);

    return () => {
    }
  })
  const scrollEle = useRef();
  let startNodeele = Math.max(0, Math.floor(scrollTop / +props.rowHeight));

  let visibleItems = props.items.slice(startNodeele, startNodeele + props.visibleItemsLength);
  let transformValue = `translateY(${startNodeele * +props.rowHeight}px)`;
  console.log('rendering', Math.random());

  const scroll = () => {
    setScrollTop(scrollEle.current.scrollTop)

  }
  const scrollContainerStyle = {
    height: props.height,
    overflowY: "scroll"
  }
  const totalHeightStyle = { height: totalHeight };
  const mainContainerStyle = { transform: transformValue }
  return (
    <div className="scrollContainer" ref={scrollEle} style={scrollContainerStyle}>
      <div style={totalHeightStyle}>
        <div className="main-container" style={mainContainerStyle}>
          {visibleItems}
        </div>
      </div>
    </div>
  )
}


export default React.memo(VirtualScroll);

let’s understand the code

You guys need to understand one thing here, we have did some math to calculate which elements can be visible in viewport and which element can be removed.

 const scrollContainerStyle = {
    height: props.height,
    overflowY: "scroll"
  }
  const totalHeightStyle = { height: totalHeight };
  const mainContainerStyle = { transform: transformValue }
  return (
    <div className="scrollContainer" ref={scrollEle} style={scrollContainerStyle}>
      <div style={totalHeightStyle}>
        <div className="main-container" style={mainContainerStyle}>
          {visibleItems}
        </div>
      </div>
    </div>
  )

As you can see in above code, we have three parent div then inside that we are rendering items

First parent div, is where we going to add scroll event listner and overflowY with scroll

Second div, Its height is (individual element height * total elements). For eg, If we have 100,000 rows and each row height is 70px then total element height will be (100000 * 70)px

The third div, we are going to show a limited number of row for eg. 50 rows at a time, then obviously all elements will position to the start of the div and scroll will be at current scroll position, technically we achieve to show only a few rows but we need to position rows correctly so whenever we scroll then it would be visible to the end-user, so that’s why we added transform: translateY() property, it will position the third div vertically and the value for this is starting position of the visible row

let startNodeele = Math.max(0,Math.floor(scrollTop / +props.rowHeight));

In the above code, we did some math to calculate the startNodeele , scrollTop is nothing but current scroll position and we are dividing it with individual rowHeight

If you are react geek, I think above code is well explanatory to understand

Using Virtual scroll Component

App.js

import React, { useState } from 'react';
import './App.css';
import VirtualScroll from './components/virtualScroll/VirtualScroll';

const totalEle = 100000;
const gridHeightStyle = {
height:'70px'
}
const grids = new Array(totalEle).fill(null).map((data, index) => {
  return (<div style={gridHeightStyle} key={index} className="grid">
    <h3>Lorem Ipsum is simply dummy text of the printing {index}</h3>
  </div>)
})


function App() {
 
  return (
    <VirtualScroll
    height="300px"
    totalElements = {totalEle}
    rowHeight={70}
    items={grids}
    visibleItemsLength={50}

    >
    </VirtualScroll>
  
  );
}

export default App;

As you can see, In the above code we have created an array of 100000 elements, and passed that to VirtualScroll component and also we need some more input like to height of the scroll div, individual row height, visibleItemlength, for making this component more reusable

Performance optimization

There are some specific area we need to take care of

  • we should have minimal operation in scroll function
  • You can also add throttling into the scroll function like when user stops scrolling then only calculate visible items
  • React component rendering should be minimal, as you can see in the example we are re-rendering component only when scrollTop is changing

Conclusion:

Here we developed virtual scrolling using react with some mathematical calculation. Might be you have some other approach to do virtual scroll, But I suggest implement some functionality by yourself instead of using npm package. It helps you to grow and understand the concept

If you like this post, follow me on twitter

Leave a Reply

Your email address will not be published. Required fields are marked *