diff --git a/web/default/src/components/data-table/core/data-table-row.tsx b/web/default/src/components/data-table/core/data-table-row.tsx index 456ee87b..808398c0 100644 --- a/web/default/src/components/data-table/core/data-table-row.tsx +++ b/web/default/src/components/data-table/core/data-table-row.tsx @@ -35,18 +35,25 @@ type DataTableRowProps = { cellRenderColumns?: TanstackTable['options']['columns'] } & Omit, 'children'> +type DataTableRowInnerProps = DataTableRowProps & { + isSelected: boolean +} + function DataTableRowInner({ row, + isSelected, className, getColumnClassName, cellRenderColumns, ...rowProps -}: DataTableRowProps) { +}: DataTableRowInnerProps) { + // Destructured only to keep it out of `rowProps` (it is not a valid DOM attr) + // and to feed the memo comparator below; it is intentionally unused here. void cellRenderColumns return ( @@ -65,20 +72,30 @@ function DataTableRowInner({ ) } -export const DataTableRow = React.memo(DataTableRowInner, (prev, next) => { - // Skip re-render when only the getColumnClassName reference changed but the - // row identity and selection state are the same — callers rarely stabilize - // this callback, so excluding it from comparison avoids unnecessary renders. - // Column cell renderers can close over external state while the row stays - // stable, so column definitions are part of the render identity. +const MemoizedDataTableRow = React.memo(DataTableRowInner, (prev, next) => { + // Do not read row.getIsSelected() inside the comparator: TanStack row objects + // keep a stable reference while their selection state mutates, so reading it + // here compares identical live values and misses selection changes. Selection + // is lifted to the `isSelected` prop, captured per render in DataTableRow. + // + // Column cell renderers (and getColumnClassName) can close over external + // state while the row stays stable, so column definitions and the class + // resolver are part of the render identity and must be compared too. return ( prev.row === next.row && prev.className === next.className && - prev.row.getIsSelected() === next.row.getIsSelected() && + prev.isSelected === next.isSelected && + prev.getColumnClassName === next.getColumnClassName && prev.cellRenderColumns === next.cellRenderColumns ) }) as typeof DataTableRowInner +export function DataTableRow(props: DataTableRowProps) { + return ( + + ) +} + function renderCellContent(cell: Cell) { const content = flexRender(cell.column.columnDef.cell, cell.getContext()) const textContent = getPrimitiveTextContent(content) @@ -102,4 +119,4 @@ function getPrimitiveTextContent(content: React.ReactNode): string | null { } return null -} +} \ No newline at end of file