简体   繁体   English

如何将错误边界包装在获取数据的 react-table 组件周围?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM