refactor(DataTable): optimize processed data handling and improve warning for missing IDs; streamline DataTableComponents imports

This commit is contained in:
Marco Beretta 2025-09-26 18:51:45 +02:00
parent 1fa6d3db27
commit b4aa36b6f1
No known key found for this signature in database
GPG key ID: D918033D8E74CC11
2 changed files with 37 additions and 32 deletions

View file

@ -10,7 +10,7 @@ import {
type ColumnDef,
type Row,
} from '@tanstack/react-table';
import type { DataTableProps, ProcessedDataRow, TableColumnDef } from './DataTable.types';
import type { DataTableProps, ProcessedDataRow } from './DataTable.types';
import { SelectionCheckbox, MemoizedTableRow, SkeletonRows } from './DataTableComponents';
import { Table, TableBody, TableHead, TableHeader, TableCell, TableRow } from '../Table';
import { useDebounced, useOptimizedRowSelection } from './DataTable.hooks';
@ -75,6 +75,17 @@ function DataTable<TData extends Record<string, unknown>, TValue>({
const debouncedTerm = useDebounced(searchTerm, debounceDelay);
const finalSorting = sorting ?? internalSorting;
const processedData = useMemo<ProcessedDataRow<TData>[]>(() => {
return data.map((item, index) => {
const id = item.id;
return {
...item,
_id: String(id ?? `row-${index}`),
_index: index,
};
});
}, [data]);
const calculatedVisibility = useMemo(() => {
const newVisibility: VisibilityState = {};
columns.forEach((col) => {
@ -101,28 +112,24 @@ function DataTable<TData extends Record<string, unknown>, TValue>({
setColumnVisibility((prev) => ({ ...prev, ...calculatedVisibility }));
}, [calculatedVisibility]);
const processedData = useMemo(
() =>
data.map((item, index): ProcessedDataRow<TData> => {
if (item.id === null || item.id === undefined) {
logger.warn(
'DataTable Warning: A data row is missing a unique "id" property. Using index as a fallback. This can lead to unexpected behavior with selection and sorting.',
item,
);
}
const hasWarnedAboutMissingIds = useRef(false);
return {
...item,
_index: index,
_id: String(item.id ?? `row-${index}`),
};
}),
[data],
);
useEffect(() => {
if (data.length > 0 && !hasWarnedAboutMissingIds.current) {
const missing = data.filter((item) => item.id === null || item.id === undefined);
if (missing.length > 0) {
logger.warn(
`DataTable Warning: ${missing.length} data rows are missing a unique "id" property. Using index as a fallback. This can lead to unexpected behavior with selection and sorting.`,
{ missingCount: missing.length, sample: missing.slice(0, 3) },
);
hasWarnedAboutMissingIds.current = true;
}
}
}, [data]);
const tableColumns = useMemo((): TableColumnDef<TData, TValue>[] => {
const tableColumns = useMemo((): ColumnDef<ProcessedDataRow<TData>, TValue>[] => {
if (!enableRowSelection || !showCheckboxes) {
return columns as TableColumnDef<TData, TValue>[];
return columns.map((col) => col as unknown as ColumnDef<ProcessedDataRow<TData>, TValue>);
}
const selectColumn: ColumnDef<ProcessedDataRow<TData>, TValue> = {
@ -148,9 +155,12 @@ function DataTable<TData extends Record<string, unknown>, TValue>({
meta: {
className: 'w-12',
},
} as TableColumnDef<TData, TValue>;
};
return [selectColumn, ...(columns as TableColumnDef<TData, TValue>[])];
return [
selectColumn,
...columns.map((col) => col as unknown as ColumnDef<ProcessedDataRow<TData>, TValue>),
];
}, [columns, enableRowSelection, showCheckboxes, localize]);
const table = useReactTable<ProcessedDataRow<TData>>({
@ -240,7 +250,7 @@ function DataTable<TData extends Record<string, unknown>, TValue>({
}
scrollTimeoutRef.current = null;
}, 150);
}, 50);
scrollRAFRef.current = null;
});
@ -288,7 +298,7 @@ function DataTable<TData extends Record<string, unknown>, TValue>({
{customActionsRenderer &&
customActionsRenderer({
selectedCount,
selectedRows: table.getSelectedRowModel().rows.map((r) => r.original),
selectedRows: table.getSelectedRowModel().rows.map((r) => r.original as TData),
table,
})}
</div>
@ -375,9 +385,7 @@ function DataTable<TData extends Record<string, unknown>, TValue>({
return (
<MemoizedTableRow
key={virtualRow.key}
row={row as Row<ProcessedDataRow<TData>>}
columns={tableColumns as ColumnDef<Record<string, unknown>>[]}
index={virtualRow.index}
row={row as unknown as Row<TData>}
virtualIndex={virtualRow.index}
/>
);

View file

@ -1,7 +1,7 @@
import { memo } from 'react';
import { flexRender } from '@tanstack/react-table';
import type { Row, ColumnDef } from '@tanstack/react-table';
import type { TableColumn } from './DataTable.types';
import type { Row } from '@tanstack/react-table';
import { TableCell, TableRow } from '../Table';
import { Checkbox } from '../Checkbox';
import { Skeleton } from '../Skeleton';
@ -45,8 +45,6 @@ const TableRowComponent = <TData extends Record<string, unknown>>({
virtualIndex,
}: {
row: Row<TData>;
columns: ColumnDef<TData, unknown>[];
index: number;
virtualIndex?: number;
}) => (
<TableRow
@ -80,8 +78,7 @@ export const MemoizedTableRow = memo(
TableRowComponent,
(prev, next) =>
prev.row.original === next.row.original &&
prev.row.getIsSelected() === next.row.getIsSelected() &&
prev.columns === next.columns,
prev.row.getIsSelected() === next.row.getIsSelected(),
);
export const SkeletonRows = memo(