ankeetmaini / react-infinite-scroll-component

An awesome Infinite Scroll component in react.
https://react-infinite-scroll-component.netlify.com/
MIT License
2.88k stars 322 forks source link

loader doesn't appear when page is bigger than InfiniteScroll #345

Open nik0145 opened 2 years ago

nik0145 commented 2 years ago

demo if you scroll to the end the loader will appear. How to fix it?

bencehusi commented 1 year ago

I also have this issue... is there any solution?

My version is "react-infinite-scroll-component": "^6.1.0".

Thanks!

HironTez commented 1 year ago

react-infinite-scroll-hook doesn't have this problem. You can use it directly in your component or create a separate InfiniteScroll component like this:

import { useEffect, useState } from "react";
import useInfiniteScroll from "react-infinite-scroll-hook";

type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Pick<
  T,
  Exclude<keyof T, Keys>
> &
  {
    [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>;
  }[Keys];

type HasMore = {
  hasMore?: boolean;
  totalCount?: number;
};

interface IProps {
  loadMore: (page?: number) => void;
  loader?: JSX.Element;
  default?: JSX.Element;
  children?: JSX.Element[];
}

type Props = IProps & RequireAtLeastOne<HasMore, keyof HasMore>;

/**
  A component for implementing infinite scrolling in React.
  * @component
  * @param {Function} props.loadMore - The function to be called when more data is needed. It should accept an optional page number as its parameter.
  * @param {boolean} [props.hasMore] - A boolean flag indicating whether there are more items to load. Required if `totalCount` is not provided.
  * @param {number} [props.totalCount] - The total number of items that can be loaded. Required if `hasMore` is not provided.
  * @param {JSX.Element} [props.loader] - The loader element to be displayed while loading more data. If not provided, a default circular progress animation will be used.
  * @param {JSX.Element} [props.default] - An element to show when there's no items to scroll.
  * @return {JSX.Element} The InfiniteScroll component.
  * @example
  * <div style={{overflowY: "auto", height: "400px"}}>
  *   <InfiniteScroll loadMore={fetchMoreItems} totalCount={meta.total}>
  *     {items.map(item => <div>{item}<div>)}
  *   </InfiniteScroll>
  * </div>
  */
const InfiniteScroll = (props: Props): JSX.Element => {
  const [loading, setLoading] = useState(false);

  const hasMore =
    props.totalCount && props.children?.length
      ? props.totalCount > props.children.length
      : props.hasMore;

  const [sentryRef] = useInfiniteScroll({
    loading,
    hasNextPage: hasMore ?? false,
    onLoadMore: () => {
      setLoading(true);
      props.loadMore();
    },
  });

  useEffect(() => {
    if (loading) setLoading(false);
  }, [props.children]);

  return (
    <>
      {
        props.children?.length
          ? props.children // Children
          : props.default // Default element
      }
      {
        // Loading
        hasMore && (
          <div ref={sentryRef}>
            {props.loader || (
              <div>
                Loading...
              </div>
            )}
          </div>
        )
      }
    </>
  );
};

export default InfiniteScroll;