When a checkbox is checked in a child subTable, I want those values to be applied in the parent row column. But I can't think of a way to do it. I need help...
I used expending and sub component together in React-table v8 to create the code below.
The problem is that when I check the checkbox in the corresponding parent row column, the table inside the sub component also displays the
indeterminate, checked, onChange
etc events should be triggered as well.
Since I have a lot of row data, it seems difficult to use state as well, is there a good way?
Like the image, the parent checkbox should be controlled as well.
table.tsx
import { ExpandedState, flexRender, getCoreRowModel, getExpandedRowModel, useReactTable } from '@tanstack/react-table';
import { useVirtual } from '@tanstack/react-virtual';
import { Checkbox } from 'antd';
import { Fragment, MouseEvent, useRef, useState } from 'react';
import { UnitSpin } from '../spin';
import { cssObj } from './style';
import { SubTable } from './subTable';
import { ColoColumnDef, UnitReactTableProps } from './type';
export const UnitReactTable = <T extends object & { subRows?: T[] }>({
columns,
data,
isLoading,
containerHeight,
rowDoubleClick,
selectRows,
subColumns,
isCheckBox,
}: UnitReactTableProps<T>) => {
const [rowSelection, setRowSelection] = useState({});
const [expanded, setExpanded] = useState<ExpandedState>({});
const tableContainerRef = useRef<HTMLDivElement | null>(null);
const columnsWithExpending = () => {
if (subColumns && isCheckBox)
return [
{
accessorKey: 'subRows',
size: 48,
header: () => null,
cell: ({ row }) => (
<div css={cssObj.subDepthBox(row.depth)}>
{row.getCanExpand() && (
<button type="button" onClick={row.getToggleExpandedHandler()}>
{row.getIsExpanded() ? '-' : '+'}
</button>
)}
</div>
),
},
{
id: 'select',
size: 48,
alignCenter: true,
header: ({ table }) => (
<Checkbox
checked={table.getIsAllRowsSelected()}
indeterminate={table.getIsSomeRowsSelected()}
onChange={table.getToggleAllRowsSelectedHandler()}
/>
),
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
disabled={!row.getCanSelect()}
indeterminate={row.getIsSomeSelected()}
onChange={row.getToggleSelectedHandler()}
/>
),
},
...columns,
] as ColoColumnDef<T>[];
return columns;
};
const reactTable = useReactTable({
columns: columnsWithExpending(),
data: data || [],
state: {
expanded,
rowSelection,
},
getRowCanExpand: () => true,
onExpandedChange: setExpanded,
getExpandedRowModel: getExpandedRowModel(),
enableRowSelection: true,
onRowSelectionChange: setRowSelection,
getCoreRowModel: getCoreRowModel(),
});
const { rows } = reactTable.getRowModel();
const rowVirtualizer = useVirtual({
parentRef: tableContainerRef,
size: rows.length,
overscan: 20,
});
const { virtualItems: virtualRows, totalSize } = rowVirtualizer;
const paddingTop = virtualRows.length > 0 ? virtualRows?.[0]?.start || 0 : 0;
const paddingBottom = virtualRows.length > 0 ? totalSize - (virtualRows?.[virtualRows.length - 1]?.end || 0) : 0;
selectRows?.(reactTable.getSelectedRowModel().flatRows.map((item) => item.original));
return (
<div css={cssObj.wrapper}>
{isLoading && <UnitSpin size="large" />}
<div css={cssObj.spaceBox} />
<div ref={tableContainerRef} css={cssObj.containerBox(containerHeight)}>
<table css={cssObj.tableBox}>
<thead>
{reactTable.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th key={header.id} colSpan={header.colSpan} style={{ width: header.getSize() }}>
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
</th>
))}
</tr>
))}
</thead>
<tbody>
{paddingTop > 0 && (
<tr>
<td style={{ height: `${paddingTop}px` }} />
</tr>
)}
{virtualRows.map((virtualRow) => {
return (
<Fragment key={row.id}>
<tr>
{row.getVisibleCells().map((cell) => {
const { alignCenter } = cell.column.columnDef as ColoColumnDef<T>;
return (
<td key={cell.id} css={cssObj.subBox}>
<div css={cssObj.tdBox(alignCenter)}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</div>
</td>
);
})}
</tr>
{row.getIsExpanded() && (
<tr>
<td colSpan={row.getVisibleCells().length}>
<SubTable rowData={row} subColumns={subColumns} />
</td>
</tr>
)}
</Fragment>
);
})}
{paddingBottom > 0 && (
<tr>
<td style={{ height: `${paddingBottom}px` }} />
</tr>
)}
</tbody>
</table>
</div>
</div>
);
};
subTable
/* eslint-disable react/no-unstable-nested-components */
import { flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table';
import { Checkbox } from 'antd';
import { ColoColumnDef, SubTableDef } from './type';
export const SubTable = <T extends object & { subRows: T[] }>({ rowData, subColumns }: SubTableDef<T>) => {
const subColumnsData: ColoColumnDef<T>[] = [
{
id: 'select',
size: 48,
alignCenter: true,
header: ({ table }) => (
<Checkbox
checked={table.getIsAllRowsSelected()}
indeterminate={table.getIsSomeRowsSelected()}
onChange={table.getToggleAllRowsSelectedHandler()}
/>
),
cell: ({ row }) => (
<Checkbox checked={row.getIsSelected()} indeterminate={row.getIsSomeSelected()} onChange={row.getToggleSelectedHandler()} />
),
},
...(subColumns as ColoColumnDef<T>[]),
];
const subTable = useReactTable({
columns: subColumnsData,
data: rowData.original.subRows || [],
state: {},
getCoreRowModel: getCoreRowModel(),
});
return (
<div style={{ backgroundColor: '#fafafa', paddingLeft: 48 }}>
<table>
.... table
</table>
</div>
);
};