React-table hooks.visibleColumns.push 调用外部和内部之间的数据流

React-table data flow between outside and inside of hooks.visibleColumns.push invocation

我想在 useTable 调用之外创建一个数组,操作该数组,并根据数组状态选中或取消选中复选框。每次单击按钮后,通过向数组添加一个元素来增加数组的长度。当长度大于 3 时,input 应该被检查。

问题是 inputchecked 属性内外数组状态不同。在外面它按预期工作:数组长度增加。在内部,数组长度等于初始长度 0。

我附上了带有一些日志记录的代码。我认为相关部分可能以 useTable 调用结束(然后是我从 react-table 文档中获取的一些代码,其中包含按钮和模拟数据,添加了列)。我应该对代码进行哪些更改才能使其按预期工作?

import React, { useMemo, useState } from 'react';
import { useTable } from 'react-table'

function Table({ columns, data }) {

  // neither stateArr nor simpleArr help reach what I would like to
  const [stateArr, setStateArr] = useState([]);
  let simpleArr = [...stateArr];

  const handleOnButtonClick = () => {
    console.log("Outside checked: simpleArr, stateArr");
    console.log(simpleArr);
    console.log(stateArr);
    setStateArr([...stateArr, 1]);
    // in this case unnecessary, since, as I understand, simpleArr is rendered and (re)assigned above
    // simpleArr = [...stateArr];
  };

  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
  } = useTable({
    columns,
    data,
  },
  (hooks) => {
    hooks.visibleColumns.push((columns) => {
      return [
        {
          id: 'checkedInputs',
          Header: () => {
            return (<div>
              <input type="checkbox"
                   // working, not most elegant way to combine logging and computing boolean
                   checked={console.log("Inside checked: simpleArr, stateArr") || console.log(simpleArr)
                     || console.log(stateArr) || simpleArr.length > 3 || stateArr.length > 3} />
            </div>);
          },
          Cell: () => {
            return (<div>R</div>);
          },
        },
        ...columns,
        ];
    });
  }
  );

  return (
    <div>
    <table {...getTableProps()}>
      <thead>
        {headerGroups.map(headerGroup => (
          <tr {...headerGroup.getHeaderGroupProps()}>
            {headerGroup.headers.map(column => (
              <th {...column.getHeaderProps()}>{column.render('Header')}</th>
            ))}
          </tr>
        ))}
      </thead>
      <tbody {...getTableBodyProps()}>
        {rows.map((row, i) => {
          prepareRow(row)
          return (
            <tr {...row.getRowProps()}>
              {row.cells.map(cell => {
                return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
              })}
            </tr>
          )
        })}
      </tbody>
    </table>
    <button type="button" onClick={handleOnButtonClick}>Click Me!</button>
    </div>
  )
}

function App() {
  const columns = useMemo(
    () => [
      {
        Header: 'Animal Type',
        accessor: 'animalType',
      },
      {
        Header: 'Number of legs',
        accessor: 'numberOfLegs',
      },
    ],
    [],
  );

  const data = useMemo(
    () => [
      {
        animalType: 'dog',
        numberOfLegs: 4,
      },
      {
        animalType: 'snake',
        numberOfLegs: 0,
      },
    ],
    [],
  );

  return (
    <Table columns={columns} data={data} />
  )
}

export default App;

陈旧数据

创建 table 时调用 hooks.visibleColumns.push 函数一次。它创建了一个 Header 渲染组件,它接受一些道具和 returns 一个 JSX 元素。每次 table 更新时,都会调用基于这些属性 呈现 Header 的函数。 创建这个Header组件的函数被称为once

在您的示例中,您创建了一个 Header 组件,该组件根据 simpleArrstateArr 的值打印出一些数据创建,而不是在调用时创建。

Table 州

如果我们希望 Header 组件使用当前数据进行渲染,那么我们应该从道具中获取该数据。 Header 被调用时有很多道具,但我们将使用的是 state,这是 table 的状态。我们将 table 的 initialState 设置为对象 { stateArr: [] }。这与标准 table 状态 { hiddenColumns: [] }.

合并

table 状态通过 useReducer 挂钩更新,因此我们通过 disptach 执行操作来更新它。我们需要自定义 stateReducer 来根据 action.

的内容更新 table 状态
import React, { useMemo } from "react";
import { useTable } from "react-table";

function Table({ columns, data }) {
  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
    dispatch,
    state
  } = useTable(
    {
      columns,
      data,
      initialState: {
        stateArr: []
      },
      stateReducer: (newState, action, prevState) => {
        console.log(action, newState, newState.stateArr);
        switch (action.type) {
          case "incrementChecks":
            return {
              ...newState,
              stateArr: [...newState.stateArr, action.payload]
            };
          default:
            return newState;
        }
      }
    },
    (hooks) => {
      hooks.visibleColumns.push((columns) => {
        return [
          {
            id: "checkedInputs",
            Header: (props) => {
              console.log("header props", props); // so you can see all the data you get
              console.log("stateArr", props.state.stateArr);
              return (
                <input
                  type="checkbox"
                  readOnly
                  checked={props.state.stateArr.length > 3}
                />
              );
            },
            Cell: () => {
              return <div>R</div>;
            }
          },
          ...columns
        ];
      });
    }
  );

  const handleOnButtonClick = () => {
    // payload is the item which we are appending to the array
    dispatch({ type: "incrementChecks", payload: 1 });
  };

  console.log("stateArr", state.stateArr);

  return (
    <div>
      <table {...getTableProps()}>
        <thead>
          {headerGroups.map((headerGroup) => (
            <tr {...headerGroup.getHeaderGroupProps()}>
              {headerGroup.headers.map((column) => (
                <th {...column.getHeaderProps()}>{column.render("Header")}</th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody {...getTableBodyProps()}>
          {rows.map((row, i) => {
            prepareRow(row);
            return (
              <tr {...row.getRowProps()}>
                {row.cells.map((cell) => {
                  return (
                    <td {...cell.getCellProps()}>{cell.render("Cell")}</td>
                  );
                })}
              </tr>
            );
          })}
        </tbody>
      </table>
      <button type="button" onClick={handleOnButtonClick}>
        Append
      </button>
    </div>
  );
}

function App() {
  const columns = useMemo(
    () => [
      {
        Header: "Animal Type",
        accessor: "animalType"
      },
      {
        Header: "Number of legs",
        accessor: "numberOfLegs"
      }
    ],
    []
  );

  const data = useMemo(
    () => [
      {
        animalType: "dog",
        numberOfLegs: 4
      },
      {
        animalType: "snake",
        numberOfLegs: 0
      }
    ],
    []
  );

  return <Table columns={columns} data={data} />;
}

export default App;

CodeSandbox Link