From 6405479a36e3a2013fe27087b1e7fe6b8ce2375f Mon Sep 17 00:00:00 2001 From: Max Patiiuk Date: Mon, 10 Oct 2022 13:07:53 -0500 Subject: [PATCH] Fix an infinite fetch loop in FormTable Fixes #2309 --- .../js_src/lib/components/formtable.tsx | 8 +++---- .../lib/components/queryresultstable.tsx | 6 ++--- .../lib/components/useInfiniteScroll.tsx | 22 +++++++++---------- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/formtable.tsx b/specifyweb/frontend/js_src/lib/components/formtable.tsx index 9db24833fd4..86e69c9dabd 100644 --- a/specifyweb/frontend/js_src/lib/components/formtable.tsx +++ b/specifyweb/frontend/js_src/lib/components/formtable.tsx @@ -142,10 +142,10 @@ export function FormTable({ const headerIsVisible = resources.length !== 1 || !isExpanded[resources[0].cid]; - const scrollerRef = React.useRef(null); + const [scroller, setScroller] = React.useState(null); const { isFetching, handleScroll } = useInfiniteScroll( handleFetchMore, - scrollerRef + scroller ); // FEATURE: add for formTable records when expanded @@ -176,7 +176,7 @@ export function FormTable({ }`, maxHeight: `${maxHeight}px`, }} - forwardRef={scrollerRef} + forwardRef={setScroller} onScroll={handleScroll} >
@@ -474,7 +474,7 @@ export function FormTableCollection({ setRecords(Array.from(collection.models)); handleDelete?.(resource); }} - onFetchMore={handleFetchMore} + onFetchMore={collection.isComplete() ? undefined : handleFetchMore} {...props} /> ); diff --git a/specifyweb/frontend/js_src/lib/components/queryresultstable.tsx b/specifyweb/frontend/js_src/lib/components/queryresultstable.tsx index afa15ce9226..ae8c11afef6 100644 --- a/specifyweb/frontend/js_src/lib/components/queryresultstable.tsx +++ b/specifyweb/frontend/js_src/lib/components/queryresultstable.tsx @@ -350,10 +350,10 @@ export function QueryResultsTable({ treeRanksLoaded === true; const canFetchMore = !Array.isArray(results) || results.length !== totalCount; - const scrollRef = React.useRef(null); + const [scroller, setScroller] = React.useState(null); const { isFetching, handleScroll } = useInfiniteScroll( canFetchMore ? handleFetchMore : undefined, - scrollRef + scroller ); return ( @@ -433,7 +433,7 @@ export function QueryResultsTable({ '--columns': fieldSpecs.length, } as React.CSSProperties } - ref={scrollRef} + ref={setScroller} onScroll={ showResults && (isFetching || !canFetchMore) ? undefined diff --git a/specifyweb/frontend/js_src/lib/components/useInfiniteScroll.tsx b/specifyweb/frontend/js_src/lib/components/useInfiniteScroll.tsx index a8467f01c5c..14f21cca5b7 100644 --- a/specifyweb/frontend/js_src/lib/components/useInfiniteScroll.tsx +++ b/specifyweb/frontend/js_src/lib/components/useInfiniteScroll.tsx @@ -8,7 +8,7 @@ import { useBooleanState } from './hooks'; */ export function useInfiniteScroll( handleFetch: (() => Promise) | undefined, - scrollerRef: React.RefObject + scroller: HTMLElement | null ): { readonly isFetching: boolean; readonly handleScroll: (event: React.UIEvent) => void; @@ -21,19 +21,17 @@ export function useInfiniteScroll( handleFetching(); await handleFetch(); isFetchingRef.current = false; - handleFetched(); + await new Promise((resolve) => setTimeout(resolve, 0)); // Fetch until there is a scroll bar - setTimeout( - (): void => - scrollerRef.current !== null && - scrollerRef.current.scrollWidth !== scrollerRef.current.clientWidth - ? void doFetch().catch(crash) - : undefined, - 0 - ); - }, [handleFetch, scrollerRef, handleFetching, handleFetched]); + if (scroller !== null && scroller.scrollHeight === scroller.clientHeight) + doFetch().catch(crash); + handleFetched(); + }, [handleFetch, scroller, handleFetching, handleFetched]); - React.useEffect(() => void doFetch(), []); + React.useEffect( + () => (typeof scroller === 'object' ? void doFetch() : undefined), + [scroller] + ); return { isFetching,