首页 > 解决方案 > 如何停止共享 react-table v7 useTable 状态?

问题描述

我有一个组件可以创建一个反应表,当只涉及一个页面时,它可以按预期工作:

const ReactTable = (props) => {
    const {data, columns, viewState, updateViewState} = props;

    const defaultColumn = React.useMemo(() => ({
        Filter: DefaultColumnFilter,
        updateFiltering: (column, newValue) => updateFiltering(viewState, updateViewState, column, newValue),
        filter: 'standard',
        canResize: true}),[]);

    const {filters, filterable, groupBy, expanded, page} = viewState;

    const tableInstance = useTable(
        {
            columns,
            data,
            defaultColumn,
            autoResetExpanded: false,
            groupByFn: (rows, columnId) => {
                const getKey = value => value.label ? value.label: value;
                return rows.reduce((prev, row) => {
                    const resKey = `${getKey(row.values[columnId])}`;
                    prev[resKey] = Array.isArray(prev[resKey]) ? prev[resKey] : [];
                    prev[resKey].push(row);
                    return prev}, {})},
            filterTypes: {'standard': applyFilter},
            pageSize: page.size,
            initialState: {filters, groupBy, expanded, pageIndex: page.index}},
        useFilters,
        useGroupBy,
        useExpanded,
        usePagination,
        useRowSelect,
        addSelectColumn);

    const {getTableProps, getTableBodyProps, setGroupBy} = tableInstance;

    React.useEffect(
        () => updateViewStateFromTable(viewState, tableInstance, updateViewState));

    if (!isEqual(groupBy, tableInstance.state.groupBy)) setGroupBy(groupBy);

    const headerRows = getHeaderRows(tableInstance, viewState, updateViewState);
    const dataRows = getDataRows(tableInstance);
    const filterRow = getFilterRow(tableInstance, viewState);

    console.log('ReactTable', {props, tableInstance})
    return (
        <div>
            <table className="ReactTable -highlight rt-table"
                   {...getTableProps()}>
                <thead className="rt-thead -header">
                    {headerRows}
                </thead>
                <tbody className="rt-tbody" {...getTableBodyProps()}>
                    {(filterable)? filterRow:null}
                    {dataRows}
                </tbody>
                <PaginationFoot {...{viewState, tableInstance}} />
            </table>
            <h2>viewState:</h2>
            <pre>
                <code>
                    {JSON.stringify({viewState}, null, 4)}
                </code>
            </pre>
        </div>)};

export default ReactTable;

不幸的是,如果我更改一页上的页面索引或大小,它也会影响所有其他页面。

“viewState”是我的构造,用于跟踪和保持过滤器、分页、扩展、列选择、分组和排序(并允许用户创建自己的自定义命名视图状态)。

似乎表的内部状态正在被破坏性修改,因为 viewState 副本显示了正确的分页数据(但在渲染后更新为不正确的共享值)。

我尝试使用 useControlledState 直接进入/退出 viewState 但这会导致错误,因为它试图在渲染操作期间更新状态。

标签: reactjsreact-table-v7

解决方案


由于表状态是一种状态,因此该行为在技术上是正确的。这里的麻烦在于,当数据和viewState发生变化时,我想有效地重新挂载react table组件(或者重新初始化它的状态)。

有一个带有组件的“表格页面”,其属性正在发生变化,因此提供了不同的表格规范和数据内容。

路由将每个 /table/:tableName 路由到同一个 TablePage ,后者为其 ReactTable 组件提供数据和 viewState。因此 useTable 内部状态被重新用于每个表,这会导致问题。

一个详细的解决方法只是为每个表页面添加一个组件(而不仅仅是更改数据),从而改变我的路由表:

const routeMap = [
  {path: '/', component: Dashboard, allowAny},
  {path: '/login', component: LoginOrOut, allowAny},

  {path: "/table/:tableName", component: TablePage},
  {path: "/tables", component: Tables},
  {path: "/new/:modelType", component: RoutedModel, action: 'new'},
  ...

对此:

const routeMap = [
  {path: '/', component: Dashboard, allowAny},
  {path: '/login', component: LoginOrOut, allowAny},

  ...tableNames.map(tableName =>
      ({path: `/table/${tableName}`, component: props => <TablePage tableName={tableName} {...props} />})),

  {path: "/tables", component: Tables},
  {path: "/new/:modelType", component: RoutedModel, action: 'new'},
  ...

这感觉很笨拙,因为我正在创建一个更大的 DOM,以便组件可以保持自己的状态,但它看起来很高效。


推荐阅读