How to control a checkbox inside a nested table created with a subcomponent in React-table v8

675 Views Asked by At

enter image description here

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?

enter image description here

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>
  );
};
0

There are 0 best solutions below