Efficiently Rendering a Large Number of Records in a React Component

Efficiently Rendering a Large Number of Records in a React Component

As web applications become more complex, handling a vast amount of data efficiently is a typical challenge developers face. Rendering many records in a React component can often lead to performance issues and browser crashes. Fortunately, React offers a solution through virtual rendering techniques and viewport detection. This guide will explore the best practices for implementing this approach to ensure smooth user experiences and prevent your browser from slowing down or crashing.

Understanding the Problem

When dealing with a large dataset in a React component, the traditional approach of rendering all records simultaneously can overwhelm the browser’s rendering engine and lead to a sluggish user interface. This is especially true when dealing with hundreds or thousands of records, as the browser has to process and render each element, consuming both memory and processing power.

Introducing Virtual Rendering

Virtual rendering, also known as “lazy loading,” is a technique that optimises the rendering process by only rendering the items that are currently visible within the user’s viewport. Instead of rendering all the records at once, virtual rendering dynamically renders and updates only the elements within the screen’s visible area. This significantly reduces the strain on the browser and enhances the application’s performance.

First, we need to create a viewPort div to accomplish this in HTML. This div acts as a container for the items visible in the list. It also includes a scrollbar, allowing users to scroll through everything.

Then, inside this div, we will add another div element to act as an item container with a height equal to the size of a single row multiplied by the total number of rows. This will enable the viewport div to scroll with the appropriate scroll length.

Finally, we must apply the CSS property “overflow-y: scroll” to the viewport div to enable scrolling functionality.

So, let’s get into the code. In this article, we will create a react virtual rendering component that can be used on any project.

Okay, let’s create the component for the Viewport and the Item;

import { useRef } from 'react'; 
import './index.css'; 

const Item = ({top, itemHeight, children}) => { 
  return ( 
    <div className="item" style={{ top: top, height: itemHeight }}> 
      {children} 
    </div> 
  ); 
}; 
const Viewport = (props) => { 
  const viewPortRef = useRef(null); 
  return ( 
    <div ref={viewPortRef} className="viewPort"><div className="itemContainer"></div> 
    </div> 
  );
}; 
export default Viewport;         

and here is the index.css (this is just a basic; you can change it to fit your needs later):

.viewPort { 
  position: relative; 
  width: 100%; 
  height: 300px; 
  border: solid 1px; 
  overflow-y: scroll; 
  overflow-x: hidden; 
} 
.itemContainer { 
  position: absolute; 
  width: 100%; 
  background-color: azure; 
} 
.item { 
  position: absolute; 
  background-color: beige; 
  border: solid 1px; 
  width: 100%; 
  text-align: center; 
}         

Now, we are going to create some crucial variables for our Viewport component:

1?? The height for each row of data (itemHeight) props we will pass whenever we use this virtualised viewport component.

2?? With itemHeight, we can calculate the total height of the item container div (h=itemheight * numRows), and we are going to create a variable call (containerStyle) to use as an inline style that will update the height of the item container in CSS.

3?? numVisibleItems to use as the number of visible items in the viewPort (numVisibleItems)=viewPort.height/itemHeight (this time, we will set the viewport visible height to 300).

4??The visible range state will be used to render the items into the item container, where the default start is 0, and the end is the value of numVisibleItems.

And we need to have a render function to render our items.

So, our viewport component code will look like this:

const Viewport = ({ itemHeight, data }) => { 
  const viewPortRef = useRef(null); 
  const viewportHeight = 300;
  const numVisibleItems = Math.trunc(viewportHeight / itemHeight); 
  const [visibleRange, setVisibleRange] = useState({ start: 0, end: numVisibleItems }); 
  const containerStyle = { height: data.length * itemHeight }; 

  const renderRows = () => { 
    let result = []; 
    for (let index = visibleRange.start; index < visibleRange.end + 1; index++) { 
      result.push(<Item key={index} top={index * itemHeight} itemheight={itemHeight}>{data[index]}</Item>); 
    } 
    return result; 
  }; 
  return ( 
    <div ref={viewPortRef} className="viewPort"> 
      <div className="itemContainer" style={containerStyle}> 
        {renderRows()} 
      </div> 
    </div> 
  ); 
};         

The renderRows method is where the magic happens. We only render the currently visible elements and position each item in the correct location based on the scroll position of the viewport div.

Now, we have a viewport component that can display the first n visible items with a scroll bar. The final step is to add a listener to the scrollbar so that as it moves, we can update the state properties’ visibleRange start and end index.

Let’s create a new function in our viewport component:

const scrollPos = () => { 
    let currentIndx = Math.trunc(viewPortRef.current.scrollTop / itemHeight); 
    currentIndx = 
      currentIndx - numVisibleItems >= data.length 
        ? currentIndx - numVisibleItems 
        : currentIndx; 
    setVisibleRange({ 
      start: currentIndx,
      end: 
        currentIndx + numVisibleItems >= data.length 
          ? data.length - 1 
          : currentIndx + numVisibleItems, 
    }); 
  };        

Changing the state will trigger a re-render, and we will see the appropriate rows rendered based on the new scroll position. New items will be created, and the non-visible items will be removed; how cool is that?

So, our final viewport component will look like this:

import { useRef, useState } from "react";
import "./index.css";

const Item = ({ top, itemHeight, children }) => {
  return (
    <div className="item" style={{ top: top, height: itemHeight }}>
      {children}
    </div>
  );
};

const Viewport = ({ itemHeight, data }) => {
  const viewPortRef = useRef(null);
  const viewportHeight = 300;
  const numVisibleItems = Math.trunc(viewportHeight / itemHeight);
  const [visibleRange, setVisibleRange] = useState({
    start: 0,
    end: numVisibleItems,
  });
  const containerStyle = {
    height: data.length * itemHeight,
  };

  const renderRows = () => {
    let result = [];

    for (
      let index = visibleRange.start;
      index < visibleRange.end + 1;
      index++
    ) {
      result.push(
        <Item key={index} top={index * itemHeight} itemheight={itemHeight}>
          {data[index]}
        </Item>
      );
    }

    return result;
  };

  const scrollPos = () => {
    let currentIndex = Math.trunc(viewPortRef.current.scrollTop / itemHeight);

    currentIndex =
      currentIndex - numVisibleItems >= data.length
        ? currentIndex - numVisibleItems
        : currentIndex;

    setVisibleRange({
      start: currentIndex,
      end:
        currentIndex + numVisibleItems >= data.length
          ? data.length - 1
          : currentIndex + numVisibleItems,
    });
  };

  return (
    <div ref={viewPortRef} className="viewPort" onScroll={scrollPos}>
      <div className="itemContainer" style={containerStyle}>
        {renderRows()}
      </div>
    </div>
  );
};

export default Viewport;        

Congratulations!

You just completed creating a virtual rendering viewport component. Here is an example of an app that uses this component:

const dummyData = Array.from({ length: 1000000 }, (_, i) => `Row ${i}`); 
//we will render 1000000 rows of "Row N" text 
const App = () => 
<Viewport itemHeight={50} data={dummyData} />; 
//this example each row we set to 50px height 
export default App;         

You can see the result here . As you can see, the code is super fast, with almost no impact on the CPU when scrolling.

Using React Virtualised Libraries

Several popular libraries, such as react-window and react-virtualized, can simplify the process. These libraries provide components like List, Grid, and Table, which utilise virtual rendering techniques to efficiently handle large datasets.

Using react-window

1?? Installation: Install the react-window library using npm or yarn.

npm install react-window.        

2?? Integration: Import the necessary components from react-window and create a virtualised list.

import { FixedSizeList } from 'react-window'; 

function VirtualisedListComponent({ data }) { 
  return ( 
    <FixedSizeList 
      height={400} // Set the height of the visible area 
      itemCount={data.length} 
      itemSize={60} // Set the height of each individual item 
      width={300} // Set the width of the visible area 
    > 
      {({ index, style }) => ( 
        <div style={style}>{data[index]}</div> 
      )} 
    </FixedSizeList> 
  ); 
}         

Using react-virtualized

1?? Installation: Install the react-virtualized library using npm or yarn. 2?? Integration: Import the required components and create a virtualised list.

import { List } from 'react-virtualized'; 

function VirtualisedListComponent({ data }) { 
  return ( 
    <List 
      height={400} 
      rowCount={data.length} 
      rowHeight={60} 
      rowRenderer={({ index, key, style }) => ( 
        <div key={key} style={style}> 
          {data[index]} 
        </div> 
      )} 
      width={300} 
    /> 
  ); 
}         

Conclusion

Efficiently rendering many records in a React component is a fundamental challenge for developers. By implementing virtual rendering techniques and viewport detection, you can significantly improve the performance of your application and prevent browser slowdowns or crashes.

Utilising libraries like react-window or react-virtualized simplifies the implementation process, allowing you to focus on delivering a seamless user experience even when dealing with massive datasets. Combine virtual rendering with other performance optimisation strategies for the best results.


要查看或添加评论,请登录

社区洞察

其他会员也浏览了