[英]How can I wrap Error Boundaries around my react-table component that fetches data?
I was using this guide in the react-table documentation on fetching data.我在获取数据的 react-table 文档中使用了本指南。 An issue I'm having now is that I'm attempting to wrap an error boundary around my Table component and I get this error:
我现在遇到的一个问题是我试图在我的 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.
Of course this wasn't an issue when I wasn't fetching the data based on filters and pagination, but when I included the useeffect method of the table component, I would get infinite rerenders due to the fetchData method which alters state data, such as columns,filters, and table data.当然,当我不基于过滤器和分页获取数据时,这不是问题,但是当我包含 table 组件的 useeffect 方法时,由于 fetchData 方法会改变 state 数据,我会得到无限的重新渲染,例如作为列、过滤器和表数据。
Table Component with ErrorBoundary:带有 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>
Table Component Code:表组件代码:
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);
Don't know what's your backend hierarchy of sorting but according to the standard most of us follow is like we give property name and it sorting order and get data according to that不知道您的后端排序层次结构是什么,但根据我们大多数人遵循的标准,就像我们给出属性名称及其排序顺序并根据该标准获取数据
// try this code instead
useEffect(() => {
// let dataFilter = {
// ...filter,
// sort: sortBy,
// };
fetchData({pageIndex, pageSize, dataFilter, clientId, orgId});
}, [pageIndex, pageSize, sortBy]);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.