Commit dc9aeab9 by DawnMoon1542 Committed by GitHub

fix(data-table): update table body immediately when toggling columns (#6253)

DataTableRow's React.memo did not depend on columnVisibility.
TanStack row references stay stable when visible columns change, so the
table body did not refresh after View -> Toggle columns until another
action rebuilt the column definitions.

Capture a visible column id signature (visibleColumnIds) outside the
memo and compare it, matching the existing isSelected snapshot pattern.
All tables that use DataTableRow pick up the fix.
parent 506e41c1
...@@ -39,6 +39,12 @@ type DataTableRowProps<TData> = { ...@@ -39,6 +39,12 @@ type DataTableRowProps<TData> = {
type DataTableRowInnerProps<TData> = DataTableRowProps<TData> & { type DataTableRowInnerProps<TData> = DataTableRowProps<TData> & {
isSelected: boolean isSelected: boolean
/**
* Stable signature of currently visible leaf columns for this row.
* Captured outside the memo comparator so visibility toggles re-render
* even when the TanStack row object reference stays the same.
*/
visibleColumnIds: string
} }
function DataTableRowInner<TData>({ function DataTableRowInner<TData>({
...@@ -47,11 +53,13 @@ function DataTableRowInner<TData>({ ...@@ -47,11 +53,13 @@ function DataTableRowInner<TData>({
className, className,
getColumnClassName, getColumnClassName,
cellRenderColumns, cellRenderColumns,
visibleColumnIds,
...rowProps ...rowProps
}: DataTableRowInnerProps<TData>) { }: DataTableRowInnerProps<TData>) {
// Destructured only to keep it out of `rowProps` (it is not a valid DOM attr) // Destructured only to keep them out of `rowProps` (not valid DOM attrs)
// and to feed the memo comparator below; it is intentionally unused here. // and to feed the memo comparator below; intentionally unused here.
void cellRenderColumns void cellRenderColumns
void visibleColumnIds
return ( return (
<TableRow <TableRow
...@@ -81,10 +89,11 @@ function DataTableRowInner<TData>({ ...@@ -81,10 +89,11 @@ function DataTableRowInner<TData>({
} }
const MemoizedDataTableRow = React.memo(DataTableRowInner, (prev, next) => { const MemoizedDataTableRow = React.memo(DataTableRowInner, (prev, next) => {
// Do not read row.getIsSelected() inside the comparator: TanStack row objects // Do not read row.getIsSelected() / row.getVisibleCells() inside the
// keep a stable reference while their selection state mutates, so reading it // comparator: TanStack row objects keep a stable reference while selection
// here compares identical live values and misses selection changes. Selection // and columnVisibility mutate on the table instance. Reading them here would
// is lifted to the `isSelected` prop, captured per render in DataTableRow. // compare identical live values and miss those updates. Both are lifted to
// explicit props, captured per render in DataTableRow.
// //
// Column cell renderers (and getColumnClassName) can close over external // Column cell renderers (and getColumnClassName) can close over external
// state while the row stays stable, so column definitions and the class // state while the row stays stable, so column definitions and the class
...@@ -93,14 +102,24 @@ const MemoizedDataTableRow = React.memo(DataTableRowInner, (prev, next) => { ...@@ -93,14 +102,24 @@ const MemoizedDataTableRow = React.memo(DataTableRowInner, (prev, next) => {
prev.row === next.row && prev.row === next.row &&
prev.className === next.className && prev.className === next.className &&
prev.isSelected === next.isSelected && prev.isSelected === next.isSelected &&
prev.visibleColumnIds === next.visibleColumnIds &&
prev.getColumnClassName === next.getColumnClassName && prev.getColumnClassName === next.getColumnClassName &&
prev.cellRenderColumns === next.cellRenderColumns prev.cellRenderColumns === next.cellRenderColumns
) )
}) as typeof DataTableRowInner }) as typeof DataTableRowInner
export function DataTableRow<TData>(props: DataTableRowProps<TData>) { export function DataTableRow<TData>(props: DataTableRowProps<TData>) {
const visibleColumnIds = props.row
.getVisibleCells()
.map((cell) => cell.column.id)
.join('\0')
return ( return (
<MemoizedDataTableRow {...props} isSelected={props.row.getIsSelected()} /> <MemoizedDataTableRow
{...props}
isSelected={props.row.getIsSelected()}
visibleColumnIds={visibleColumnIds}
/>
) )
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment