From dc9aeab98bf8a11b99b5da1c76ccae153565e85e Mon Sep 17 00:00:00 2001 From: DawnMoon1542 <160485532+DawnMoon1542@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:38:16 +0800 Subject: [PATCH] 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. --- .../data-table/core/data-table-row.tsx | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) 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 137448b4..4777f9a6 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 @@ -39,6 +39,12 @@ type DataTableRowProps = { type DataTableRowInnerProps = DataTableRowProps & { 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({ @@ -47,11 +53,13 @@ function DataTableRowInner({ className, getColumnClassName, cellRenderColumns, + visibleColumnIds, ...rowProps }: 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. + // Destructured only to keep them out of `rowProps` (not valid DOM attrs) + // and to feed the memo comparator below; intentionally unused here. void cellRenderColumns + void visibleColumnIds return ( ({ } 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. + // Do not read row.getIsSelected() / row.getVisibleCells() inside the + // comparator: TanStack row objects keep a stable reference while selection + // and columnVisibility mutate on the table instance. Reading them here would + // 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 // state while the row stays stable, so column definitions and the class @@ -93,14 +102,24 @@ const MemoizedDataTableRow = React.memo(DataTableRowInner, (prev, next) => { prev.row === next.row && prev.className === next.className && prev.isSelected === next.isSelected && + prev.visibleColumnIds === next.visibleColumnIds && prev.getColumnClassName === next.getColumnClassName && prev.cellRenderColumns === next.cellRenderColumns ) }) as typeof DataTableRowInner export function DataTableRow(props: DataTableRowProps) { + const visibleColumnIds = props.row + .getVisibleCells() + .map((cell) => cell.column.id) + .join('\0') + return ( - + ) }