Reputation: 11
So I am trying to build a reusable Table component using Tanstack Table.
import { Box, Stack } from '@mui/material';
import type { SortingState, OnChangeFn, PaginationState } from '@tanstack/react-table';
import {
useReactTable,
getCoreRowModel,
flexRender,
getSortedRowModel,
getPaginationRowModel,
} from '@tanstack/react-table';
import Text from '@components/Text';
import SortIconTable from '@components/SortIconTable';
import { useMemo, useState } from 'react';
export type PaginationShape = {
cursor?: string | null | undefined;
page: number;
pageSize: number;
};
export type TableProps<TData> = {
columns: TData[];
cursor?: string | null | undefined;
loading?: boolean;
onPagination?: OnChangeFn<PaginationShape>;
onSort?: OnChangeFn<SortingState>;
pagination?: PaginationState;
rows: TData[];
};
export type TablePropsColumns<TData> = TableProps<TData>['columns'];
const pageSizes = [10, 20, 50, 100, 500];
export const Table = ({ rows, columns, onSort, onPagination, cursor }: TableProps<any>) => {
const [sorting, setSorting] = useState<SortingState>([]);
const [pagination, setPagination] = useState<PaginationState>({ pageIndex: 0, pageSize: 10 });
const memoizedRows = useMemo(() => rows, [rows]);
const memoizedColumns = useMemo(() => columns, [columns]);
const table = useReactTable({
autoResetPageIndex: true,
columnResizeMode: 'onChange',
columns: memoizedColumns,
data: memoizedRows,
debugColumns: true,
debugHeaders: true,
debugTable: true,
enableColumnFilters: true,
enableColumnResizing: true,
enableMultiSort: false,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
manualPagination: true,
manualSorting: true,
onPaginationChange: async updater => {
if (typeof updater !== 'function') return;
const newPageInfo = await updater(table.getState().pagination);
setPagination(() => newPageInfo);
const formatPaginationData = {
cursor,
page: newPageInfo?.pageIndex,
pageSize: newPageInfo?.pageSize,
};
onPagination?.(formatPaginationData);
},
onSortingChange: data => {
setSorting(data);
onSort?.(data);
},
sortDescFirst: false,
state: {
pagination,
sorting,
},
});
return (
<div className="p-2">
<Box sx={{ position: 'relative' }}>
<table style={{ maxWidth: '100%' }}>
<thead>
{table.getHeaderGroups().map(headerGroup => (
<tr key={headerGroup.id}>
{headerGroup.headers.map(header => {
const canResize = header.column.getCanResize();
const canSort = header.column.getCanSort();
return (
<th colSpan={header.colSpan} key={header.id} style={{ width: header.getSize() }}>
<Stack direction={'row'}>
{flexRender(header.column.columnDef.header, header.getContext())}
{canSort && (
<SortIconTable
onSort={() => {
table.setSorting([
{
desc: !header.column.getIsSorted(),
id: header.column.id,
},
]);
header.column.getToggleSortingHandler();
}}
sortDirection={header.column.getNextSortingOrder()}
/>
)}
{canResize && (
<Box
className={`resizer ${header.column.getIsResizing() && 'isResizing'}`}
onDoubleClick={() => header.column.resetSize()}
onMouseDown={header.getResizeHandler()}
sx={{
'&.isResizing': {
background: 'blue',
cursor: 'col-resize',
opacity: 1,
},
'&:hover': {
cursor: 'col-resize',
},
border: '1px solid gray',
ml: 'auto',
}}
/>
)}
</Stack>
</th>
);
})}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map(row => (
<tr key={row.id}>
{row.getVisibleCells().map(cell => (
<td key={cell.id} style={{ width: cell.column.getSize() }}>
<Text>{flexRender(cell.column.columnDef.cell, cell.getContext())}</Text>
</td>
))}
</tr>
))}
</tbody>
<div>
<select
onChange={e => {
table.setPagination({
pageIndex: 0,
pageSize: Number(e.target.value),
});
}}
value={table.getState().pagination.pageSize}
>
{pageSizes.map(pageSize => (
<option key={pageSize} value={pageSize}>
{pageSize}
</option>
))}
</select>
</div>
</table>
</Box>
</div>
);
};
and I am using as follow
const [sorting, setSorting] = useState<SortingState>([]);
const [pagination, setPagination] = useState<PaginationShape>({ page: 0, pageSize: 10 });
const params = {
order_by: buildSortByString(sorting),
page_size: pagination.pageSize,
};
console.log({ params });
const { data } = useQuery<EventsLogResponse>({
queryFn: () => getEventsLog({ params }),
queryKey: ['getEventsLog', { params }],
});
<CustomTable
columns={customColumns}
cursor={data?.pagination_information.next_cursor}
onPagination={setPagination}
onSort={setSorting}
rows={data?.events ?? []}
/>
I cant identify why, every time I update either the sorting or the pagination it starts an infinite loop. Any suggestions where the root cause is coming from?
I did try to clean up the state updates on both the Table component, or in the component calling the table, but the issue seems to be coming from within the table itself. I also thought it was a memo issue, and I added these two lines
const memoizedRows = useMemo(() => rows, [rows]);
const memoizedColumns = useMemo(() => columns, [columns]);
but the problem still the same.
Upvotes: 1
Views: 532