import React from 'react'; import {tableDataApi} from '../../store/reducers/tableData'; import type {IResponseError} from '../../types/api/error'; import {getArray} from '../../utils'; import {useAutoRefreshInterval} from '../../utils/hooks'; import {ResponseError} from '../Errors/ResponseError'; import {usePaginatedTableState} from './PaginatedTableContext'; import {EmptyTableRow, LoadingTableRow, TableRow} from './TableRow'; import type {PaginatedTableId} from './constants'; import {getTableChunkQueryParams} from './getTableChunkQueryParams'; import i18n from './i18n'; import type { Column, FetchData, GetRowClassName, OnRowClick, PaginatedTableData, RenderEmptyDataMessage, RenderErrorMessage, SortParams, } from './types'; import {typedMemo} from './utils'; const DEBOUNCE_TIMEOUT = 200; interface TableChunkProps { id: number; chunkSize: number; rowHeight: number; calculatedCount: number; columns: Column[]; filters?: F; sortParams?: SortParams; shouldFetch: boolean; shouldRender: boolean; tableName: PaginatedTableId; fetchData: FetchData; getRowClassName?: GetRowClassName; onRowClick?: OnRowClick; renderErrorMessage?: RenderErrorMessage; renderEmptyDataMessage?: RenderEmptyDataMessage; onDataFetched: (data: PaginatedTableData) => void; keepCache?: boolean; } // Memoisation prevents chunks rerenders that could cause perfomance issues on big tables export const TableChunk = typedMemo(function TableChunk({ id, chunkSize, calculatedCount, rowHeight, columns, fetchData, tableName, filters, sortParams, getRowClassName, onRowClick, renderErrorMessage, renderEmptyDataMessage, onDataFetched, shouldFetch, shouldRender, keepCache, }: TableChunkProps) { const [isTimeoutActive, setIsTimeoutActive] = React.useState(true); const [autoRefreshInterval] = useAutoRefreshInterval(); const {noBatching} = usePaginatedTableState(); const queryParams = getTableChunkQueryParams({ offset: id * chunkSize, limit: chunkSize, fetchData, filters, sortParams, columns, tableName, noBatching, }); tableDataApi.useFetchTableChunkQuery(queryParams, { skip: isTimeoutActive || !shouldFetch, pollingInterval: autoRefreshInterval, refetchOnMountOrArgChange: !keepCache, }); const {currentData, error} = tableDataApi.endpoints.fetchTableChunk.useQueryState(queryParams); React.useEffect(() => { let timeout = 0; if (shouldFetch && isTimeoutActive) { timeout = window.setTimeout(() => { setIsTimeoutActive(false); }, DEBOUNCE_TIMEOUT); } return () => { window.clearTimeout(timeout); }; }, [shouldFetch, isTimeoutActive]); React.useLayoutEffect(() => { if (currentData) { // Consumers derive row dimensions from the response before loaded rows are painted. onDataFetched({ ...currentData, data: currentData.data as T[], found: currentData.found || 0, total: currentData.total || 0, }); } }, [currentData, onDataFetched]); const dataLength = currentData?.data?.length || calculatedCount; const renderContent = () => { if (!currentData) { if (error) { const errorData = error as IResponseError; return ( {renderErrorMessage ? ( renderErrorMessage(errorData) ) : ( )} ); } else { return getArray(dataLength).map((value) => ( )); } } // Data is loaded, but there are no entities in the chunk if (!currentData.data?.length) { return ( {renderEmptyDataMessage ? renderEmptyDataMessage() : i18n('empty')} ); } return currentData.data.map((rowData, index) => ( )); }; return shouldRender ? renderContent() : null; });