From 0a6cdb8dabba88d7cd92a1e1a69e5ca8fe3c11a7 Mon Sep 17 00:00:00 2001 From: Abhishek Tomar <104965815+abhisektomar1@users.noreply.github.com> Date: Wed, 26 Jun 2024 15:25:22 +0530 Subject: [PATCH 1/4] fix(table): resolve double fetch issue in useInfiniteScroll hook (fix #3251) --- .../hooks/use-infinite-scroll/src/index.ts | 71 +++++++++++-------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/packages/hooks/use-infinite-scroll/src/index.ts b/packages/hooks/use-infinite-scroll/src/index.ts index 7c9f0542ae..587e9c8a5b 100644 --- a/packages/hooks/use-infinite-scroll/src/index.ts +++ b/packages/hooks/use-infinite-scroll/src/index.ts @@ -1,5 +1,5 @@ import debounce from "lodash.debounce"; -import {useLayoutEffect, useRef} from "react"; +import {useLayoutEffect, useRef, useCallback} from "react"; export interface UseInfiniteScrollProps { /** @@ -27,13 +27,28 @@ export interface UseInfiniteScrollProps { } export function useInfiniteScroll(props: UseInfiniteScrollProps = {}) { - const {hasMore, distance = 250, isEnabled = true, shouldUseLoader = true, onLoadMore} = props; + const { + hasMore = true, + distance = 250, + isEnabled = true, + shouldUseLoader = true, + onLoadMore, + } = props; const scrollContainerRef = useRef(null); const loaderRef = useRef(null); - - const previousY = useRef(); - const previousRatio = useRef(0); + const observerRef = useRef(null); + const isLoadingRef = useRef(false); + + const loadMore = useCallback(() => { + if (!isLoadingRef.current && hasMore && onLoadMore) { + isLoadingRef.current = true; + onLoadMore(); + setTimeout(() => { + isLoadingRef.current = false; + }, 100); // Debounce time to prevent multiple calls + } + }, [hasMore, onLoadMore]); useLayoutEffect(() => { const scrollContainerNode = scrollContainerRef.current; @@ -48,50 +63,44 @@ export function useInfiniteScroll(props: UseInfiniteScrollProps = {}) { const options = { root: scrollContainerNode, rootMargin: `0px 0px ${distance}px 0px`, + threshold: 0.1, }; - const listener = (entries: IntersectionObserverEntry[]) => { - entries.forEach(({isIntersecting, intersectionRatio, boundingClientRect = {}}) => { - const y = boundingClientRect.y || 0; - - if ( - isIntersecting && - intersectionRatio >= previousRatio.current && - (!previousY.current || y < previousY.current) - ) { - onLoadMore?.(); - } - previousY.current = y; - previousRatio.current = intersectionRatio; - }); - }; + const observer = new IntersectionObserver((entries) => { + const [entry] = entries; - const observer = new IntersectionObserver(listener, options); + if (entry.isIntersecting) { + loadMore(); + } + }, options); observer.observe(loaderNode); + observerRef.current = observer; - return () => observer.disconnect(); + return () => { + if (observerRef.current) { + observerRef.current.disconnect(); + } + }; } else { - const debouncedOnLoadMore = onLoadMore ? debounce(onLoadMore, 200) : undefined; - - const checkIfNearBottom = () => { + const debouncedCheckIfNearBottom = debounce(() => { if ( scrollContainerNode.scrollHeight - scrollContainerNode.scrollTop <= scrollContainerNode.clientHeight + distance ) { - debouncedOnLoadMore?.(); + loadMore(); } - }; + }, 100); - scrollContainerNode.addEventListener("scroll", checkIfNearBottom); + scrollContainerNode.addEventListener("scroll", debouncedCheckIfNearBottom); return () => { - scrollContainerNode.removeEventListener("scroll", checkIfNearBottom); + scrollContainerNode.removeEventListener("scroll", debouncedCheckIfNearBottom); }; } - }, [hasMore, distance, isEnabled, onLoadMore, shouldUseLoader]); + }, [hasMore, distance, isEnabled, shouldUseLoader, loadMore]); - return [loaderRef, scrollContainerRef]; + return [loaderRef, scrollContainerRef] as const; } export type UseInfiniteScrollReturn = ReturnType; From 044b3f9b84ac53558bc90c77e1015ed367b32094 Mon Sep 17 00:00:00 2001 From: Abhishek Tomar <104965815+abhisektomar1@users.noreply.github.com> Date: Wed, 26 Jun 2024 16:03:38 +0530 Subject: [PATCH 2/4] fix(table): remove unnecessary else clause --- .../hooks/use-infinite-scroll/src/index.ts | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/hooks/use-infinite-scroll/src/index.ts b/packages/hooks/use-infinite-scroll/src/index.ts index 587e9c8a5b..dc025bd1e6 100644 --- a/packages/hooks/use-infinite-scroll/src/index.ts +++ b/packages/hooks/use-infinite-scroll/src/index.ts @@ -82,22 +82,22 @@ export function useInfiniteScroll(props: UseInfiniteScrollProps = {}) { observerRef.current.disconnect(); } }; - } else { - const debouncedCheckIfNearBottom = debounce(() => { - if ( - scrollContainerNode.scrollHeight - scrollContainerNode.scrollTop <= - scrollContainerNode.clientHeight + distance - ) { - loadMore(); - } - }, 100); + } - scrollContainerNode.addEventListener("scroll", debouncedCheckIfNearBottom); + const debouncedCheckIfNearBottom = debounce(() => { + if ( + scrollContainerNode.scrollHeight - scrollContainerNode.scrollTop <= + scrollContainerNode.clientHeight + distance + ) { + loadMore(); + } + }, 100); - return () => { - scrollContainerNode.removeEventListener("scroll", debouncedCheckIfNearBottom); - }; - } + scrollContainerNode.addEventListener("scroll", debouncedCheckIfNearBottom); + + return () => { + scrollContainerNode.removeEventListener("scroll", debouncedCheckIfNearBottom); + }; }, [hasMore, distance, isEnabled, shouldUseLoader, loadMore]); return [loaderRef, scrollContainerRef] as const; From f8c7b6eb5b1214ea3badb9df1845757042a80f02 Mon Sep 17 00:00:00 2001 From: Abhishek Tomar <104965815+abhisektomar1@users.noreply.github.com> Date: Wed, 26 Jun 2024 16:20:38 +0530 Subject: [PATCH 3/4] fix(table): add a changeset file for use-infinite-scroll --- .changeset/fix-infinite-scroll.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fix-infinite-scroll.md diff --git a/.changeset/fix-infinite-scroll.md b/.changeset/fix-infinite-scroll.md new file mode 100644 index 0000000000..31645ad548 --- /dev/null +++ b/.changeset/fix-infinite-scroll.md @@ -0,0 +1,5 @@ +--- +"@nextui-org/use-infinite-scroll": patch +--- + +fix(table): resolve double fetch issue in useInfiniteScroll hook (#3251) \ No newline at end of file From 35f54edcba6c248ffbf8d05d495146c05c125776 Mon Sep 17 00:00:00 2001 From: WK Wong Date: Sat, 6 Jul 2024 22:17:43 +0800 Subject: [PATCH 4/4] fix(hooks): add clearTimeout function --- packages/hooks/use-infinite-scroll/src/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/hooks/use-infinite-scroll/src/index.ts b/packages/hooks/use-infinite-scroll/src/index.ts index dc025bd1e6..5c897fd063 100644 --- a/packages/hooks/use-infinite-scroll/src/index.ts +++ b/packages/hooks/use-infinite-scroll/src/index.ts @@ -41,13 +41,17 @@ export function useInfiniteScroll(props: UseInfiniteScrollProps = {}) { const isLoadingRef = useRef(false); const loadMore = useCallback(() => { + let timer: ReturnType; + if (!isLoadingRef.current && hasMore && onLoadMore) { isLoadingRef.current = true; onLoadMore(); - setTimeout(() => { + timer = setTimeout(() => { isLoadingRef.current = false; }, 100); // Debounce time to prevent multiple calls } + + return () => clearTimeout(timer); }, [hasMore, onLoadMore]); useLayoutEffect(() => {