Implementing virtual scroll using react
Tags: react, javascript
Published: 2020-06-27 14:09:59

This post first published on
<a class="wp-block-button__link has-text-color has-very-light-gray-color" href="https://www.techboxweb.com/implementing-virtual-scroll-using-react/" target="_blank" >TechBoxWeb</a>

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>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</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>So, here virtual scroll concept comes for rescue. </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>without wasting time let's implement virtual scroll using react</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Find Github and Demo here</p>
<!-- /wp:paragraph -->


<div class="wp-block-buttons aligncenter"><!-- wp:button {"textColor":"very-light-gray"} -->
<div class="wp-block-button"><a class="wp-block-button__link has-text-color has-very-light-gray-color" href="https://github.com/nitinhepat/virtual-scroll-with-react" target="_blank" rel="noreferrer noopener">Github Repo</a></div>



<div class="wp-block-button is-style-outline"><a class="wp-block-button__link has-text-color has-very-light-gray-color" href="https://virtualscroll-7b282.web.app/" target="_blank" rel="noreferrer noopener">Demo</a></div>
</div>


<!-- wp:paragraph -->
<p><strong>Contents:</strong></p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>What we will going to do?</li><li>Setting up a react project</li><li>creating virtual scroll component</li><li>performance optimization</li></ul>
<!-- /wp:list -->

<!-- wp:heading -->
<h2>What we will going to do</h2>
<!-- /wp:heading -->

<!-- wp:image {"id":752,"sizeSlug":"large"} -->
<figure class="wp-block-image size-large"><img src="https://www.techboxweb.com/wp-content/uploads/2020/06/Total-elements-1024x576.png" alt="Virtual scroll using react" class="wp-image-752"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>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</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>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</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2>Setting up a react project</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>you can create initial setup for react using below command</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>npx create-react-app reacct-virtual-scroll</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>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 <a href="https://reactjs.org/docs/create-a-new-react-app.html" target="_blank" rel="noreferrer noopener">post</a></p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2>Creating a virtual scroll component</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { useRef, useState, useEffect } from 'react';

const VirtualScroll = (props) => {
  const totalHeight = +props.rowHeight * +props.totalElements + 'px';
  const &#91;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 (
    &lt;div className="scrollContainer" ref={scrollEle} style={scrollContainerStyle}>
      &lt;div style={totalHeightStyle}>
        &lt;div className="main-container" style={mainContainerStyle}>
          {visibleItems}
        &lt;/div>
      &lt;/div>
    &lt;/div>
  )
}


export default React.memo(VirtualScroll);</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>let's understand the code</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

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

<!-- wp:paragraph -->
<p> As you can see in above code, we have three parent div then inside that we are rendering items</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>First parent div, is where we going to add scroll event listner and overflowY with scroll </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>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</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>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</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>let startNodeele = Math.max(0,Math.floor(scrollTop / +props.rowHeight));</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>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</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>If you are react geek, I think above code is well explanatory to understand</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2>Using Virtual scroll Component</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>App.js</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>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 (&lt;div style={gridHeightStyle} key={index} className="grid">
    &lt;h3>Lorem Ipsum is simply dummy text of the printing {index}&lt;/h3>
  &lt;/div>)
})


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

    >
    &lt;/VirtualScroll>
  
  );
}

export default App;
</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>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</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2>Performance optimization</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>There are some specific area we need to take care of</p>
<!-- /wp:paragraph -->

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

<!-- wp:paragraph -->
<p></p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2>Conclusion:</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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 the npm package. It helps you to grow and understand the concept</p>
<!-- /wp:paragraph -->