我在获取数据的 react-table 文档中使用了本指南。我现在遇到的一个问题是我试图在我的 Table 组件周围包裹一个错误边界,我收到了这个错误:
Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
当然,当我不基于过滤器和分页获取数据时,这不是问题,但是当我包含 table 组件的 useeffect 方法时,由于 fetchData 方法会改变状态数据,我会得到无限的重新渲染,例如作为列、过滤器和表数据。
带有 ErrorBoundary 的表组件:
<ErrorBoundary key={generateStringId()} fallbackRender={props =>
<ErrorFallback {...props} error_code={PAGE_LOAD}/>
}>
<Table
columns={columns}
data={data}
fetchData={fetchData}
status={status}
pageCount={pageCount}
availableFilters={availableFilters}
activeFilters={activeFilters}
/>
</ErrorBoundary>
表组件代码:
import React, {useEffect, useState, useCallback} from 'react';
import {useSortBy,usePagination,useTable,useRowSelect} from "react-table";
import {ErrorBoundary} from "react-error-boundary";
import ErrorFallback from "./ErrorFallback";
import SearchFilterBox from './sub_components/SearchFilterBox'
import {DATATABLE} from "../ErrorCodes";
import "./../css/datatables.scss";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
function Table({
columns,
data,
fetchData,
loading,
clientId,
orgId,
updateSettingData,
updateCellData,
deleteCellData,
activeFilters,
skipPageReset,
availableFilters = {},
tableSettings = {},
useFilters = true,
displayPagination = true,
displayFooter = false,
displayRowSelect = false,
rowSelectAction,
rowSelectTitle = '',
hiddenColumns= ["metadata"],
pageCount: controlledPageCount,
paginationOptions = [10,25,50,100],
contentBesideFilters,
}) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
footerGroups,
prepareRow,
page,
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
initialState,
selectedFlatRows,
// Get the state from the instance
state: { pageIndex, pageSize, sortBy},
} = useTable(
{
columns,
data,
availableFilters,
updateSettingData,
updateCellData,
deleteCellData,
autoResetPage: !skipPageReset,
initialState: {
pageIndex: 0,
hiddenColumns: 'metadata',
sortBy: [{
id: tableSettings['defaultSort'],
desc: false
}],
}, // Pass our hoisted table state
manualPagination: true, // Tell the usePagination
// hook that we'll handle our own data fetching
// This means we'll also have to provide our own
// pageCount.
pageCount: controlledPageCount,
manualSortBy: true,
},
useSortBy,
usePagination,
);
const [filter, setFilter] = useState({
search : {
term : '',
fields : ''
},
dateRange: {
field: '',
start: '',
end: '',
period: ''
},
sort: '',
});
if (availableFilters.search !== undefined) {
console.log("Filters Defined!");
if(availableFilters.search.columns.length <= 0) {
console.log("useFilter set to False!")
useFilters = false;
}
}
// Listen for changes in pagination and use the state to fetch our new data
useEffect(() => {
let dataFilter = {
...filter,
sort: sortBy,
};
if (typeof fetchData === 'function') {
fetchData({pageIndex, pageSize, dataFilter, clientId, orgId});
}
}, [fetchData, pageIndex, pageSize, filter, sortBy, clientId, orgId]);
const updateFilters = useCallback((filters) => {
setFilter(filters);
});
// Render the table
return (
<>
<div className="filterContainer">
<ErrorBoundary
fallbackRender={props => <ErrorFallback {...props} error_code={DATATABLE}/>}
>
<SearchFilterBox
availableFilters={availableFilters}
activeFilters={activeFilters}
onChange={filters => updateFilters(filters)}
contentBesideFilters={contentBesideFilters}
settings={tableSettings}
useFilters={useFilters}
/>
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
{column.render('Header')} {filter.search.fields.includes(column.id)
? <FontAwesomeIcon icon={['fas', 'search']} className={"searchable"}/>
: ''}
<span>
{column.isSorted
? column.isSortedDesc
? <FontAwesomeIcon icon={['fa','caret-up']}/>
: <FontAwesomeIcon icon={['fa','caret-down']}/>
: ''}
</span>
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map((row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
let mobileRowName = cell.getCellProps().key.split("_");
let classes = mobileRowName[2] === 'Actions' ? 'actionsCol' : '';
return <td className={classes} data-label={mobileRowName[2]} {...cell.getCellProps()}>{cell.render('Cell')}</td>
})}
</tr>
)
})}
</tbody>
<tfoot>
{
displayFooter &&
footerGroups &&
footerGroups.map(group => (
<tr {...group.getFooterGroupProps()} style={{"fontWeight":"bold"}}>
{group.headers.map(column => (
<td {...column.getFooterProps()}>{column.render('Footer')}</td>
))}
</tr>
))
}
{
displayPagination &&
(loading ?
(<tr><td colSpan="10000">Loading...</td></tr>) :
(<tr><td colSpan="10000">
Showing {page.length} of about {controlledPageCount * pageSize}{' '}
results
</td></tr>)
)
}
</tfoot>
</table>
{
//todo batch download functionality
displayRowSelect &&
<button
className={`download-documents btn-${selectedFlatRows.length ===0 ?'disabled':'primary'}`}
disabled={selectedFlatRows.length === 0}
onClick={() => rowSelectAction(selectedFlatRows)}>
{rowSelectTitle}
</button>
}
{/*
Pagination can be built however you'd like.
This is just a very basic UI implementation:
*/}
{displayPagination &&
<div className="pagination">
<button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
<FontAwesomeIcon icon={['fas','angle-double-left']}/>
</button>{' '}
<button onClick={() => previousPage()} disabled={!canPreviousPage}>
<FontAwesomeIcon icon={['fas','angle-left']}/>
</button>{' '}
<button onClick={() => nextPage()} disabled={!canNextPage}>
<FontAwesomeIcon icon={['fas','angle-right']}/>
</button>{' '}
<button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
<FontAwesomeIcon icon={['fas','angle-double-right']}/>
</button>{' '}
<span>
Page{' '}
<strong>
{pageIndex + 1} of {pageOptions.length}
</strong>{' '}
</span>
<span>
| 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' }}
/>
</span>{' '}
{ paginationOptions.length > 1 &&<select
value={pageSize}
onChange={e => {
setPageSize(Number(e.target.value))
}}
>
{paginationOptions.map(pageSize => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>}
</div>
}
</ErrorBoundary>
</div>
</>
)
}
export default React.memo(Table);