Rendering large lists in React efficiently is crucial for maintaining performance and ensuring a smooth user experience. Here are some best practices and techniques to achieve this:
What it is: Instead of rendering all items in the list at once, only render the items that are visible on the screen. As the user scrolls, dynamically render new items and remove off-screen items.
Libraries: Use libraries like react-window or react-virtualized.
What it is: Load and display a subset of the list at a time. When the user requests more items (e.g., by clicking a "Load More" button), fetch and render the next subset.
When to use: When the list is too large to render all at once, but virtualization is not necessary.
What it is: Use React.memo
to prevent unnecessary re-renders of list items when the parent component re-renders.
When to use: When list items are expensive to render and don't change often.
What it is: Always use a unique key
prop for each list item to help React identify which items have changed, been added, or been removed.
When to use: Always when rendering lists.
What it is: Load list items asynchronously as they come into view.
When to use: When the list data is fetched from an API and you want to load data on demand.
react-window
for Virtualizationimport React from 'react'; import { FixedSizeList as List } from 'react-window'; const LargeList = ({ data }) => { const Row = ({ index, style }) => ( <div style={style}> {/* Render your list item here */} {data[index]} </div> ); return ( <List height={500} // Height of the visible list itemCount={data.length} // Total number of items itemSize={50} // Height of each item width={300} // Width of the list > {Row} </List> ); }; export default LargeList;
import React, { useState } from 'react'; const PaginatedList = ({ data, itemsPerPage }) => { const [currentPage, setCurrentPage] = useState(1); const indexOfLastItem = currentPage * itemsPerPage; const indexOfFirstItem = indexOfLastItem - itemsPerPage; const currentItems = data.slice(indexOfFirstItem, indexOfLastItem); const paginate = (pageNumber) => setCurrentPage(pageNumber); return ( <div> <ul> {currentItems.map((item, index) => ( <li key={index}>{item}</li> ))} </ul> <div> {Array.from({ length: Math.ceil(data.length / itemsPerPage) }, (_, i) => ( <button key={i + 1} onClick={() => paginate(i + 1)}> {i + 1} </button> ))} </div> </div> ); }; export default PaginatedList;
React.memo
import React, { memo } from 'react'; const ListItem = memo(({ item }) => { return <div>{item}</div>; }); const LargeList = ({ data }) => { return ( <ul> {data.map((item, index) => ( <ListItem key={index} item={item} /> ))} </ul> ); }; export default LargeList;
import React, { useState, useEffect, useRef } from 'react'; const LazyLoadList = ({ data }) => { const [visibleItems, setVisibleItems] = useState(data.slice(0, 20)); const loaderRef = useRef(null); useEffect(() => { const observer = new IntersectionObserver( (entries) => { if (entries[0].isIntersecting) { setVisibleItems((prev) => [ ...prev, ...data.slice(prev.length, prev.length + 20), ]); } }, { threshold: 1.0 } ); if (loaderRef.current) { observer.observe(loaderRef.current); } return () => { if (loaderRef.current) { observer.unobserve(loaderRef.current); } }; }, [data]); return ( <div> <ul> {visibleItems.map((item, index) => ( <li key={index}>{item}</li> ))} </ul> <div ref={loaderRef}>Loading more...</div> </div> ); }; export default
Keep Healthy and Happy Coding! ๐