1

I am using React-table for one of my projects. I implemented the row selection as mentioned in the docs.

below is my table.js

import React, { useEffect, useMemo } from 'react'
import { useTable, usePagination, useRowSelect } from 'react-table';
import {Button, Dropdown, DropdownButton, Form} from 'react-bootstrap';
import BTable from 'react-bootstrap/Table';

export function Table({ columns, data, fetchData, noDataMessage, pageCount: controlledPageCount, loading, rowSelectionEnabled, getSelectedRows }) {
const memoizedColumns = useMemo(() => columns, [columns]);
  const memoizedData = useMemo(() => data, [data]);
  // Use the state and functions returned from useTable to build your UI
  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
    page,
    canPreviousPage,
    canNextPage,
    pageOptions,
    pageCount,
    gotoPage,
    nextPage,
    previousPage,
    setPageSize,
    selectedFlatRows,
    isAllRowsSelected,
    state: { pageIndex, pageSize, selectedRowIds },
  } = useTable({
    columns: memoizedColumns,
    data: memoizedData,
    noDataMessage,
    initialState: { pageIndex: 0, pageSize: 15 },
    manualPagination: true,
    pageCount: controlledPageCount,
  },usePagination, useRowSelect,
  hooks => {
    rowSelectionEnabled && hooks.visibleColumns.push(columns => [
      // Let's make a column for selection
      {
        id: 'selection',
        // The header can use the table's getToggleAllRowsSelectedProps method
        // to render a checkbox
        Header: ({ getToggleAllRowsSelectedProps }) => (
          <div>
            <IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
          </div>
        ),
        // The cell can use the individual row's getToggleRowSelectedProps method
        // to the render a checkbox
        Cell: ({ row }) => (
          <div>
            <IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
          </div>
        ),
      },
      ...columns,
    ])
  }
  )

  const setSize = (selectedVal) => {
    setPageSize(selectedVal);
  }

  // const fetchDataDebounced = useAsyncDebounce(fetchData, 100);

  React.useEffect(() => {
    data.length > 0 && fetchData && fetchData({ pageIndex, pageSize })
  }, [fetchData, pageIndex, pageSize])

  // React.useEffect(() => {
  //   data.length === 0 && gotoPage(0)
  // }, [data])

  // Render the UI for your table
  return (
      <>
    <BTable striped bordered loading={true} minRows={0} className='border-radius-important' {...getTableProps()}>
      <thead>
        {headerGroups.map(headerGroup => (
          <tr {...headerGroup.getHeaderGroupProps()}>
            {headerGroup.headers.map(column => (
              <th {...column.getHeaderProps()}>{column.render('Header')}</th>
            ))}
          </tr>
        ))}
      </thead>
      <tbody {...getTableBodyProps()}>
        {page.length > 0 ? (page.map((row, i) => {
          prepareRow(row)
          return (
            <tr {...row.getRowProps()}>
              {row.cells.map(cell => {
                return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
              })}
            </tr>
          )
        })) : <tr style={{height: '350px'}}><td colSpan={10} style={{verticalAlign: 'middle'}}><NoDataComponent message={noDataMessage}/></td></tr>}
      </tbody>
    </BTable>
    { pageCount > 1 ? (<div className="pagination">
        <div style={{width: '100%', paddingTop: '15px'}}>
    <div style={{display: 'flex', float: 'right'}}>
    <div style={{display: 'block'}}>
        <div>
    <Button variant='outline-primary' onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
      {'<<'}
    </Button>{' '}
    <Button variant='outline-primary' onClick={() => previousPage()} disabled={!canPreviousPage}>
      {'<'}
    </Button>{' '}
    <Button variant='outline-primary' onClick={() => nextPage()} disabled={!canNextPage}>
      {'>'}
    </Button>{' '}
    <Button variant='outline-primary' onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
      {'>>'}
    </Button>{' '}
    </div>
    <div>
      Page{' '}
      <strong>
        {pageIndex + 1} of {pageOptions.length}
      </strong>{' '}
    </div>
    </div>
    {/* <div>
      | Go to page:{' '}
      <input
        type="number"
        defaultValue={pageIndex + 1}
        onChange={e => {
          const page = e.target.value ? Number(e.target.value) - 1 : 0
          gotoPage(page)
        }}
        style={{ width: '100px' }}
      />
    </div>{' '} */}
    </div>
    <DropdownButton
    style={{float: 'left'}}
      title={pageSize}
      onSelect={setSize}
    >
      {[10, 20, 30, 40, 50].map(pageSize => (
        <Dropdown.Item eventKey={pageSize}>
          {pageSize}
        </Dropdown.Item>
      ))}
    </DropdownButton>
  </div>
  </div>) : null }
  {getSelectedRows && getSelectedRows(selectedRowIds, selectedFlatRows, isAllRowsSelected)}
  </>
  )
}

const NoDataComponent = props => (
  // <NoData noDataTitle="This is the dynamic title" {...props} />
  <div style={{width: '100%', height: '100%', textAlign: 'center', fontWeight: '700', fontSize: 'large'}}>{props.message}</div>
);

const IndeterminateCheckbox = React.forwardRef(
  ({ indeterminate, ...rest }, ref) => {
    const defaultRef = React.useRef()
    const resolvedRef = ref || defaultRef

    React.useEffect(() => {
      resolvedRef.current.indeterminate = indeterminate
    }, [resolvedRef, indeterminate])

    return (
      <>
        <Form.Check type="checkbox" ref={resolvedRef} {...rest} />
      </>
    )
  }
)

and the following is the code where i am using this table

  getSelectedRows = (rowIds, rowData, isAllRowsSelected) => {
    this.setState({selectedData: rowData});
  }

<Table columns={this.state.columns} data={this.state.data} rowSelectionEnabled={true} noDataMessage='There is no data to display' getSelectedRows={this.getSelectedRows} fetchData={this.fetchData} pageCount={this.state.count}></Table>

So whenever the checkbox is selected, following line gets triggered in table.js:

{getSelectedRows && getSelectedRows(selectedRowIds, selectedFlatRows, isAllRowsSelected)}

and getSelectedRows function gets called in my component.

But after that page crashes with the error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate

I read somewhere that memoizing data and columns will fix the issue as it is going into loop rendering again and again. So I memoized data and columns at the start of table.js file. But still the issue is there.

Can someone please help on this. Thanks in advance!

Ashu
  • 77
  • 5

0 Answers0