Skip to content

Commit

Permalink
imrpove other tables with useTableInfiniteScroll hook
Browse files Browse the repository at this point in the history
  • Loading branch information
bigabig committed Oct 16, 2024
1 parent a50820e commit 559960d
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
MaterialReactTable,
useMaterialReactTable,
} from "material-react-table";
import { useCallback, useEffect, useMemo, useRef, useState, type UIEvent } from "react";
import { useEffect, useMemo, useRef, useState, type UIEvent } from "react";
import { AnnotatedImageResult } from "../../../api/openapi/models/AnnotatedImageResult.ts";
import { AnnotatedImagesColumns } from "../../../api/openapi/models/AnnotatedImagesColumns.ts";
import { AttachedObjectType } from "../../../api/openapi/models/AttachedObjectType.ts";
Expand All @@ -19,6 +19,7 @@ import { SortDirection } from "../../../api/openapi/models/SortDirection.ts";
import { AnalysisService } from "../../../api/openapi/services/AnalysisService.ts";
import { useAuth } from "../../../auth/useAuth.ts";
import { useAppSelector } from "../../../plugins/ReduxHooks.ts";
import { useTableInfiniteScroll } from "../../../utils/useTableInfiniteScroll.ts";
import ImageCropper from "../../../views/whiteboard/nodes/ImageCropper.tsx";
import CodeRenderer from "../../Code/CodeRenderer.tsx";
import { MyFilter, createEmptyFilter } from "../../FilterDialog/filterUtils.ts";
Expand All @@ -30,6 +31,7 @@ import BBoxToolbar, { BBoxToolbarProps } from "./BBoxToolbar.tsx";
import { useInitBBoxFilterSlice } from "./useInitBBoxFilterSlice.ts";

const fetchSize = 20;
const flatMapData = (page: AnnotatedImageResult) => page.data;

export interface BBoxAnnotationTableProps {
title?: string;
Expand Down Expand Up @@ -78,7 +80,6 @@ function BBoxAnnotationTable({
const filter = useAppSelector((state) => state.bboxFilter.filter[filterName]) || createEmptyFilter(filterName);

// virtualization
const tableContainerRef = useRef<HTMLDivElement>(null);
const rowVirtualizerInstanceRef = useRef<MRT_RowVirtualizer>(null);

// table columns
Expand Down Expand Up @@ -194,37 +195,26 @@ function BBoxAnnotationTable({
},
refetchOnWindowFocus: false,
});
// create a flat array of data mapped from id to row
const flatData = useMemo(() => data?.pages.flatMap((page) => page.data) ?? [], [data]);
const totalDBRowCount = data?.pages?.[0]?.total_results ?? 0;
const totalFetched = flatData.length;

// infinite scrolling
// called on scroll and possibly on mount to fetch more data as the user scrolls and reaches bottom of table
const fetchMoreOnBottomReached = useCallback(
(containerRefElement?: HTMLDivElement | null) => {
if (containerRefElement) {
const { scrollHeight, scrollTop, clientHeight } = containerRefElement;
// once the user has scrolled within 400px of the bottom of the table, fetch more data if we can
if (scrollHeight - scrollTop - clientHeight < 400 && !isFetching && totalFetched < totalDBRowCount) {
fetchNextPage();
}
}
},
[fetchNextPage, isFetching, totalFetched, totalDBRowCount],
);
// scroll to top of table when userId, sorting or filters change
const tableContainerRef = useRef<HTMLDivElement>(null);
const { flatData, totalResults, totalFetched, fetchMoreOnScroll } = useTableInfiniteScroll({
tableContainerRef,
data,
isFetching,
fetchNextPage,
flatMapData,
});

// infinite scrolling reset:
// scroll to top of table when sorting or userId changes
useEffect(() => {
try {
rowVirtualizerInstanceRef.current?.scrollToIndex?.(0);
} catch (error) {
console.error(error);
}
}, [projectId, selectedUserId, sortingModel]);
// a check on mount to see if the table is already scrolled to the bottom and immediately needs to fetch more data
useEffect(() => {
fetchMoreOnBottomReached(tableContainerRef.current);
}, [fetchMoreOnBottomReached]);

// table
const table = useMaterialReactTable<BBoxAnnotationTableRow>({
Expand Down Expand Up @@ -264,7 +254,7 @@ function BBoxAnnotationTable({
},
muiTableContainerProps: {
ref: tableContainerRef, //get access to the table container element
onScroll: (event: UIEvent<HTMLDivElement>) => fetchMoreOnBottomReached(event.target as HTMLDivElement), //add an event listener to the table container element
onScroll: (event: UIEvent<HTMLDivElement>) => fetchMoreOnScroll(event.target as HTMLDivElement), //add an event listener to the table container element
style: { flexGrow: 1 },
},
muiToolbarAlertBannerProps: isError
Expand Down Expand Up @@ -296,7 +286,7 @@ function BBoxAnnotationTable({
renderBottomToolbarCustomActions: (props) => (
<Stack direction={"row"} spacing={1} alignItems="center">
<Typography>
Fetched {totalFetched} of {totalDBRowCount} total rows.
Fetched {totalFetched} of {totalResults} total rows.
</Typography>
{renderBottomToolbarCustomActions &&
renderBottomToolbarCustomActions({
Expand Down
42 changes: 16 additions & 26 deletions frontend/src/components/SourceDocument/SdocTable/SdocTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
MaterialReactTable,
useMaterialReactTable,
} from "material-react-table";
import { useCallback, useEffect, useMemo, useRef, useState, type UIEvent } from "react";
import { useEffect, useMemo, useRef, useState, type UIEvent } from "react";
import { ElasticSearchDocumentHit } from "../../../api/openapi/models/ElasticSearchDocumentHit.ts";
import { PaginatedElasticSearchDocumentHits } from "../../../api/openapi/models/PaginatedElasticSearchDocumentHits.ts";
import { SearchColumns } from "../../../api/openapi/models/SearchColumns.ts";
Expand All @@ -21,6 +21,7 @@ import { SearchService } from "../../../api/openapi/services/SearchService.ts";
import { useAuth } from "../../../auth/useAuth.ts";
import { useAppSelector } from "../../../plugins/ReduxHooks.ts";
import { RootState } from "../../../store/store.ts";
import { useTableInfiniteScroll } from "../../../utils/useTableInfiniteScroll.ts";
import { FilterActions, FilterState } from "../../FilterDialog/filterSlice.ts";
import { MyFilter, createEmptyFilter } from "../../FilterDialog/filterUtils.ts";
import SdocMetadataRenderer from "../../Metadata/SdocMetadataRenderer.tsx";
Expand All @@ -32,6 +33,7 @@ import { DocumentTableFilterActions } from "./documentTableFilterSlice.ts";
import { useInitDocumentTableFilterSlice } from "./useInitDocumentTableFilterSlice.ts";

const fetchSize = 20;
const flatMapData = (page: PaginatedElasticSearchDocumentHits) => page.hits;

export interface DocumentTableActionProps {
table: MRT_TableInstance<ElasticSearchDocumentHit>;
Expand Down Expand Up @@ -84,7 +86,6 @@ function SdocTable({
useAppSelector((state) => filterStateSelector(state).filter[filterName]) || createEmptyFilter(filterName);

// virtualization
const tableContainerRef = useRef<HTMLDivElement>(null);
const rowVirtualizerInstanceRef = useRef<MRT_RowVirtualizer>(null);

// table columns
Expand Down Expand Up @@ -215,37 +216,26 @@ function SdocTable({
},
refetchOnWindowFocus: false,
});
// create a flat array of data mapped from id to row
const flatData = useMemo(() => data?.pages.flatMap((page) => page.hits) ?? [], [data]);
const totalDBRowCount = data?.pages?.[0]?.total_results ?? 0;
const totalFetched = flatData.length;

// infinite scrolling
// called on scroll and possibly on mount to fetch more data as the user scrolls and reaches bottom of table
const fetchMoreOnBottomReached = useCallback(
(containerRefElement?: HTMLDivElement | null) => {
if (containerRefElement) {
const { scrollHeight, scrollTop, clientHeight } = containerRefElement;
// once the user has scrolled within 400px of the bottom of the table, fetch more data if we can
if (scrollHeight - scrollTop - clientHeight < 400 && !isFetching && totalFetched < totalDBRowCount) {
fetchNextPage();
}
}
},
[fetchNextPage, isFetching, totalFetched, totalDBRowCount],
);
// scroll to top of table when userId, sorting or filters change
const tableContainerRef = useRef<HTMLDivElement>(null);
const { flatData, totalResults, totalFetched, fetchMoreOnScroll } = useTableInfiniteScroll({
tableContainerRef,
data,
isFetching,
fetchNextPage,
flatMapData,
});

// infinite scrolling reset:
// scroll to top of table when sorting or filters change
useEffect(() => {
try {
rowVirtualizerInstanceRef.current?.scrollToIndex?.(0);
} catch (error) {
console.error(error);
}
}, [projectId, sortingModel]);
// a check on mount to see if the table is already scrolled to the bottom and immediately needs to fetch more data
useEffect(() => {
fetchMoreOnBottomReached(tableContainerRef.current);
}, [fetchMoreOnBottomReached]);

// table
const table = useMaterialReactTable<ElasticSearchDocumentHit>({
Expand Down Expand Up @@ -302,7 +292,7 @@ function SdocTable({
},
muiTableContainerProps: {
ref: tableContainerRef, //get access to the table container element
onScroll: (event: UIEvent<HTMLDivElement>) => fetchMoreOnBottomReached(event.target as HTMLDivElement), //add an event listener to the table container element
onScroll: (event: UIEvent<HTMLDivElement>) => fetchMoreOnScroll(event.target as HTMLDivElement), //add an event listener to the table container element
style: { flexGrow: 1 },
},
muiToolbarAlertBannerProps: isError
Expand Down Expand Up @@ -338,7 +328,7 @@ function SdocTable({
renderBottomToolbarCustomActions: (props) => (
<Stack direction={"row"} spacing={1} alignItems="center">
<Typography>
Fetched {totalFetched} of {totalDBRowCount} total documents.
Fetched {totalFetched} of {totalResults} total documents.
</Typography>
{renderBottomToolbarCustomActions &&
renderBottomToolbarCustomActions({
Expand Down
42 changes: 16 additions & 26 deletions frontend/src/views/analysis/WordFrequency/WordFrequencyTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
MaterialReactTable,
useMaterialReactTable,
} from "material-react-table";
import { useCallback, useEffect, useMemo, useRef, type UIEvent } from "react";
import { useEffect, useMemo, useRef, type UIEvent } from "react";
import { useParams } from "react-router-dom";
import { SortDirection } from "../../../api/openapi/models/SortDirection.ts";
import { WordFrequencyColumns } from "../../../api/openapi/models/WordFrequencyColumns.ts";
Expand All @@ -21,13 +21,15 @@ import { MyFilter } from "../../../components/FilterDialog/filterUtils.ts";
import { useAppSelector } from "../../../plugins/ReduxHooks.ts";
import { RootState } from "../../../store/store.ts";
import { useReduxConnector } from "../../../utils/useReduxConnector.ts";
import { useTableInfiniteScroll } from "../../../utils/useTableInfiniteScroll.ts";
import ExportWordFrequencyButton from "./ExportWordFrequencyButton.tsx";
import { useInitWordFrequencyFilterSlice } from "./useInitWordFrequencyFilterSlice.ts";
import { WordFrequencyActions } from "./wordFrequencySlice.ts";

const filterStateSelector = (state: RootState) => state.wordFrequency;
const filterName = "root";
const fetchSize = 20;
const flatMapData = (page: WordFrequencyResult) => page.word_frequencies;

function WordFrequencyTable() {
const projectId = parseInt(useParams<{ projectId: string }>().projectId!);
Expand All @@ -53,7 +55,6 @@ function WordFrequencyTable() {
const filter = useAppSelector((state) => state.wordFrequency.filter["root"]);

// virtualization
const tableContainerRef = useRef<HTMLDivElement>(null);
const rowVirtualizerInstanceRef = useRef<MRT_RowVirtualizer>(null);

// table columns
Expand Down Expand Up @@ -140,37 +141,26 @@ function WordFrequencyTable() {
},
refetchOnWindowFocus: false,
});
// create a flat array of data mapped from id to row
const flatData = useMemo(() => data?.pages.flatMap((page) => page.word_frequencies) ?? [], [data]);
const totalDBRowCount = data?.pages?.[0]?.total_results ?? 0;
const totalFetched = flatData.length;

// infinite scrolling
// called on scroll and possibly on mount to fetch more data as the user scrolls and reaches bottom of table
const fetchMoreOnBottomReached = useCallback(
(containerRefElement?: HTMLDivElement | null) => {
if (containerRefElement) {
const { scrollHeight, scrollTop, clientHeight } = containerRefElement;
// once the user has scrolled within 400px of the bottom of the table, fetch more data if we can
if (scrollHeight - scrollTop - clientHeight < 400 && !isFetching && totalFetched < totalDBRowCount) {
fetchNextPage();
}
}
},
[fetchNextPage, isFetching, totalFetched, totalDBRowCount],
);
// scroll to top of table when userId, sorting or filters change
const tableContainerRef = useRef<HTMLDivElement>(null);
const { flatData, totalResults, totalFetched, fetchMoreOnScroll } = useTableInfiniteScroll({
tableContainerRef,
data,
isFetching,
fetchNextPage,
flatMapData,
});

// infinite scrolling reset:
// scroll to top of table when sorting changes
useEffect(() => {
try {
rowVirtualizerInstanceRef.current?.scrollToIndex?.(0);
} catch (error) {
console.error(error);
}
}, [projectId, sortingModel]);
// a check on mount to see if the table is already scrolled to the bottom and immediately needs to fetch more data
useEffect(() => {
fetchMoreOnBottomReached(tableContainerRef.current);
}, [fetchMoreOnBottomReached]);

// table
const table = useMaterialReactTable<WordFrequencyStat>({
Expand Down Expand Up @@ -210,7 +200,7 @@ function WordFrequencyTable() {
},
muiTableContainerProps: {
ref: tableContainerRef, //get access to the table container element
onScroll: (event: UIEvent<HTMLDivElement>) => fetchMoreOnBottomReached(event.target as HTMLDivElement), //add an event listener to the table container element
onScroll: (event: UIEvent<HTMLDivElement>) => fetchMoreOnScroll(event.target as HTMLDivElement), //add an event listener to the table container element
style: { flexGrow: 1 },
},
muiToolbarAlertBannerProps: isError
Expand All @@ -224,7 +214,7 @@ function WordFrequencyTable() {
renderBottomToolbarCustomActions: () => (
<Stack direction={"row"} spacing={1} alignItems="center">
<Typography>
Fetched {totalFetched} of {totalDBRowCount} unique words (from {data?.pages?.[0]?.sdocs_total ?? 0} documents
Fetched {totalFetched} of {totalResults} unique words (from {data?.pages?.[0]?.sdocs_total ?? 0} documents
with {data?.pages?.[0]?.words_total ?? 0} words).
</Typography>
</Stack>
Expand Down
40 changes: 15 additions & 25 deletions frontend/src/views/logbook/MemoSearch/SearchMemoTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
MaterialReactTable,
useMaterialReactTable,
} from "material-react-table";
import { useCallback, useEffect, useMemo, useRef, type UIEvent } from "react";
import { useEffect, useMemo, useRef, type UIEvent } from "react";
import { ElasticSearchDocumentHit } from "../../../api/openapi/models/ElasticSearchDocumentHit.ts";
import { MemoColumns } from "../../../api/openapi/models/MemoColumns.ts";
import { PaginatedElasticSearchDocumentHits } from "../../../api/openapi/models/PaginatedElasticSearchDocumentHits.ts";
Expand All @@ -24,13 +24,15 @@ import MemoStarButton from "../../../components/Memo/MemoStarButton.tsx";
import { useAppSelector } from "../../../plugins/ReduxHooks.ts";
import { RootState } from "../../../store/store.ts";
import { useReduxConnector } from "../../../utils/useReduxConnector.ts";
import { useTableInfiniteScroll } from "../../../utils/useTableInfiniteScroll.ts";
import { LogbookActions } from "../logbookSlice.ts";
import SearchMemoOptionsMenu from "./SearchMemoOptionsMenu.tsx";
import { useInitMemoFilterSlice } from "./useInitMemoFilterSlice.ts";

const filterStateSelector = (state: RootState) => state.logbook;
const filterName = "root";
const fetchSize = 20;
const flatMapData = (page: PaginatedElasticSearchDocumentHits) => page.hits;

interface SearchMemoTableProps {
projectId: number;
Expand Down Expand Up @@ -160,38 +162,26 @@ function SearchMemoTable({ projectId }: SearchMemoTableProps) {
},
refetchOnWindowFocus: false,
});
// create a flat array of data mapped from id to row
const flatData = useMemo(() => data?.pages.flatMap((page) => page.hits) ?? [], [data]);
const totalDBRowCount = data?.pages?.[0]?.total_results ?? 0;
const totalFetched = flatData.length;

// infinite scrolling
const tableContainerRef = useRef<HTMLDivElement>(null);
// called on scroll and possibly on mount to fetch more data as the user scrolls and reaches bottom of table
const fetchMoreOnBottomReached = useCallback(
(containerRefElement?: HTMLDivElement | null) => {
if (containerRefElement) {
const { scrollHeight, scrollTop, clientHeight } = containerRefElement;
// once the user has scrolled within 400px of the bottom of the table, fetch more data if we can
if (scrollHeight - scrollTop - clientHeight < 400 && !isFetching && totalFetched < totalDBRowCount) {
fetchNextPage();
}
}
},
[fetchNextPage, isFetching, totalFetched, totalDBRowCount],
);
// scroll to top of table when sorting or filters change
const { flatData, totalResults, totalFetched, fetchMoreOnScroll } = useTableInfiniteScroll({
tableContainerRef,
data,
isFetching,
fetchNextPage,
flatMapData,
});

// infinite scrolling reset:
// scroll to top of table when sorting or filter change
useEffect(() => {
try {
rowVirtualizerInstanceRef.current?.scrollToIndex?.(0);
} catch (error) {
console.error(error);
}
}, [projectId, sortingModel, filter]);
// a check on mount to see if the table is already scrolled to the bottom and immediately needs to fetch more data
useEffect(() => {
fetchMoreOnBottomReached(tableContainerRef.current);
}, [fetchMoreOnBottomReached]);

// table
const table = useMaterialReactTable<ElasticSearchDocumentHit>({
Expand Down Expand Up @@ -245,7 +235,7 @@ function SearchMemoTable({ projectId }: SearchMemoTableProps) {
},
muiTableContainerProps: {
ref: tableContainerRef, //get access to the table container element
onScroll: (event: UIEvent<HTMLDivElement>) => fetchMoreOnBottomReached(event.target as HTMLDivElement), //add an event listener to the table container element
onScroll: (event: UIEvent<HTMLDivElement>) => fetchMoreOnScroll(event.target as HTMLDivElement), //add an event listener to the table container element
style: { flexGrow: 1 },
},
muiToolbarAlertBannerProps: isError
Expand Down Expand Up @@ -288,7 +278,7 @@ function SearchMemoTable({ projectId }: SearchMemoTableProps) {
renderBottomToolbarCustomActions: () => (
<Stack direction={"row"} spacing={1} alignItems="center">
<Typography>
Fetched {totalFetched} of {totalDBRowCount} total memos.
Fetched {totalFetched} of {totalResults} total memos.
</Typography>
</Stack>
),
Expand Down

0 comments on commit 559960d

Please sign in to comment.