reactjsreact-infinite-scroll-componentreact-infinite-scroll

React infinite-scroll with array from hook state


I'm having troubles to set up react-infinite-scroller within my React component.

I do not want to fetch data via an API directly in my component with loadMore because I already got it from my IndexedDB.

So I want to use my Array dbScans (array with objects) and want to have infinite scroll with max. 3 items of the array.

I tried to create a loadProducts function to slice and concate my array that I want to render but I am getting overload errors, when I try to call it in the Infinite-Scroll component.

import React, { useEffect, useState } from 'react';
import InfiniteScroll from 'react-infinite-scroller';

export default function ProductHistory() {
  const [dbScans, setDbScans] = useState<IProductClient[]>([]);
  const [loadedProducts, setLoadedProducts] = useState([]);

  useEffect(() => {
    (async function getDataFromDB() {
      setDbScans(await db.yourScans.toArray());
    })();
  }, []);

  let productHistory = dbScans;

  // This function is not working
  const loadProducts = (page: number) => {
    const perPage = 3;
    const moreItems: any = dbScans.slice(
      page * perPage - perPage,
      page * perPage + 1
    );
    // Am I doing this right?
    setLoadedProducts(loadedProducts.concat(moreItems));
  }

  return (
    <>
      <div className="product-history">
          { productHistory.length > 0 ?

            <InfiniteScroll
              pageStart={0}
              loadMore={loadProducts(0)} // This is not working
              hasMore={true}
              loader={<div className="loader" key={0}>Loading ...</div>}
            >

              {productHistory.map((product: any) =>
                <div className="product-history__item" key={product.id}>
                  <p>{product.name}
                </div>
              )}

              </InfiniteScroll>
            : ''
          }
      </div>
    </>
  )
}

Solution

  • You should introduce a state variable called as lastObjectPosition which will have the position of the last object that is being shown by the infinite scroll.

        const perPage = 3;
        const [lastObjectPosition , setLastObjectPosition ] = useState(0);
    

    And then hasMore attribute should be set like this:

        hasMore={lastObjectPosition < dbScans.length}
    

    And finally you should modify loadProducts function like this,

        const loadProducts = () => {
            setLoadedProducts(currentProducts => {
                return [
                    ...currentProducts,
                    dbScans.slice(lastObjectPosition, lastObjectPosition+perPage)
                ]
            });
            setLastObjectPosition(currentValue => {
                return currentValue + perPage
            }
        }