Virtualize API call for loading Large Data in React-Select with React-Window
Tags: react
Published: 2023-09-20 10:25:39

Hello internet folks! I’m pretty new to writing and sharing stuff on the internet, but I believe that by sharing our knowledge, we can build new cybernetic pyramids full of computational power! 🧑🏾‍💻🧑🏾‍💻🧑🏾‍💻

A few days ago, I was loading some data for my current clients at “CDP” where I was trying to show 1000+ data. Everything was going well until we received a requirement to scrape and find 75000+ entries and display them using React-Select, which was previously handling the 1000+ data.

Given my experience in this field, as soon as I heard the news, I knew that React wouldn’t be ideal for creating 75000+ list components to select from, especially when each of them might have their own CSS and listeners.

So what can we do? In the [React-select Advance](https://react-select.com/advanced) section, you might find a solution where you listen to keystrokes or the end of scrolling to bring in new data. However, I was thinking of pulling all the data back into my client and then processing it virtually using some other technique.

I came across some ideas on Stack Overflow that might help you. In another post, I may explain it in more detail, but for now, you can have a look at this code. It will help you with your problem.

Happy coding, and see you in the next post! 🕸🕸🕸



    `import React, { useState, useCallback, useEffect, useRef } from "react";
    import { FixedSizeList as List } from "react-window";
    import InfiniteLoader from "react-window-infinite-loader";
    
    const LOADING = 1;
    const LOADED = 2;
    
    const CustomMenuList = ({ children, initialOffset }) => {
        const itemHeight = 50;
        const [itemStatusMap, setItemStatusMap] = useState({});
    
        const itemStatusMapRef = useRef(itemStatusMap);
        useEffect(() => {
            itemStatusMapRef.current = itemStatusMap;
        }, [itemStatusMap]);
    
        const isItemLoaded = useCallback((index) => !!itemStatusMapRef.current[index], []);
    
        const loadMoreItems = useCallback((startIndex, stopIndex) => {
            setItemStatusMap((prevStatusMap) => {
                const newStatusMap = { ...prevStatusMap };
                for (let index = startIndex; index <= stopIndex; index++) {
                    newStatusMap[index] = LOADING;
                }
                return newStatusMap;
            });
    
            return new Promise((resolve) => {
                setTimeout(() => {
                    setItemStatusMap((prevStatusMap) => {
                        const newStatusMap = { ...prevStatusMap };
                        for (let index = startIndex; index <= stopIndex; index++) {
                            newStatusMap[index] = LOADED;
                        }
                        return newStatusMap;
                    });
                    resolve();
                }, 2500);
            });
        }, []);
    
        const Row = React.memo(({ index, style }) => {
            let label;
            if (itemStatusMap[index] === LOADED) {
                label = children[index];
            }
            return (
                <div style={{ ...style }}>
                    {label || "Loading..."}
                </div>
            );
        });
    
        return (
            <InfiniteLoader
                isItemLoaded={isItemLoaded}
                itemCount={children.length}
                loadMoreItems={loadMoreItems}
                threshold={5}
            >
                {({ onItemsRendered, ref }) => (
                    <List
                        height={500}
                        itemCount={children.length}
                        itemSize={itemHeight}
                        initialScrollOffset={initialOffset}
                        onItemsRendered={onItemsRendered}
                        ref={ref}
                        width="100%"
                    >
                        {Row}
                    </List>
                )}
            </InfiniteLoader>
        );
    };
    
    export default CustomMenuList;`