【问题标题】:State set method did not effect the main object - ReactJS状态设置方法不影响主对象 - ReactJS
【发布时间】:2019-08-30 20:22:30
【问题描述】:

我的项目中有三个组件,AppDataTableTable

App 将呈现一个包含 Table 组件的 DataTable。我刚刚调用了带有数据和列属性的 DataTable。

// data sample
const tmpData = [
    {
        name: "Can",
        age: 4,
    }, {
        name: "David",
        age: 44,
    }, {
        name: "Sara",
        age: 14,
    }, {
        name: "Hani",
        age: 24,
    }
]

// main columns array
const tmpColumns = [
    {
        title: "Name",
        accessor: "name",
    }, {
        title: "Age",
        accessor: "age",
    }
]

function App() {
    return (
        <div className="App" style={{background: "#f0f0f0", padding: "1rem"}}>
            <div>App Component:</div>

            <DataTable data={tmpData} columns={tmpColumns}/>
        </div>
    );
}

DataTable 组件仅用于处理我的表格上的选择和过滤操作。因此,它可以操纵数据和列。表格将在其中呈现,这是 DataTable 的代码。

function DataTable(props) {

    const [data, setData] = useState(props.data)
    const [columns, setColumns] = useState(props.columns)

    const [selection, setSelection] = useState([])

    useEffect(() => {
        // add select columns after component mount
        handleColumnChange()
    }, [])

    // listen to selection change
    useEffect(() => {

        // selection change log, It change on each select.
        console.log(selection);
    }, [selection])

    function handleRowSelect(rowName) {

        const keyIndex = selection.indexOf(rowName);

        console.log(selection, rowName, keyIndex);

        if (keyIndex === -1)
            setSelection(preSelection => ([...preSelection, ...[rowName]]))
        else
            setSelection(preSelection => preSelection.filter(sl => sl !== rowName))

    }

    function handleColumnChange() {

        // add select column if not added already
        if (!columns.find(col => col.accessor === 'select')) {

            setColumns([
                ...[{
                    title: "Select",
                    accessor: "select",
                    // this method will execute to render checkbox on Select table
                    Cell: function (row) {

                        return <input type="checkbox"
                                      onChange={() => handleRowSelect(row.name, selection)}
                                      checked={selection.includes(row.name)}/>
                    },
                }],
                ...columns,
            ])
        }
    }

    return (
        <div className="DataTable" style={{background: "#e0e0e0", padding: "1rem"}}>
            <div>Data Table:</div>

            <Table {...{data, columns}}/>
        </div>
    )
}

Table 组件将为它们呈现列和合适的数据。对于 columns 数组中的每一列,我们都有一个访问数据的项目 (accessor) 或一个返回自定义数据的可执行方法 (Cell),下面是它的代码。

function Table(props) {

    const [data, setData] = useState(props.data)

    return (
        <div className="Table" style={{background: "#d5d5d5", padding: "1rem"}}>
            <div>Table</div>

            <table>
                <tbody>
                <tr>
                    {props.columns.map((th, key) => (
                        <th key={key}>{th.title}</th>
                    ))}
                </tr>

                {/* generating data rows */}
                {data.map((dr, key) => (
                    <tr key={key}>
                        {columns.map((col, index) => (
                            <td key={index}>
                                {
                                    // the "Cell" method has high priority than "accessor" selector
                                    (col.Cell && typeof col.Cell === "function") ?
                                        col.Cell(dr) :
                                        dr[col.accessor]
                                }
                            </td>
                        ))}
                    </tr>
                ))}
                </tbody>
            </table>

        </div>
    )
}

正如您在上面看到的,为了处理行选择,我通过在列数组的第一个索引处添加一个新列来操作 DataTable 组件中的列。目前一切正常。但是,当我尝试选择一行时,选择列的调用方法无法访问我的 DataTable 组件的选择数组状态。这是我的问题!

实际上,在每次选择时,selection 数组必须更新并且目标复选框必须选中。显然,selection 的副本会更改(或者可能不会更改)。

您也可以在CodeSandbox查看整个项目

【问题讨论】:

  • setSelection 语法对于钩子是错误的。设置值直接不再调用函数
  • @Rikin,我不这么认为!即使我确实喜欢你的意思,更改后的selection 也无法在我的Cell 方法中再次访问。
  • 我现在在文档中看到它:reactjs.org/docs/… 我不知道这种回调模式,很高兴知道。感谢您指出。

标签: javascript reactjs scope react-hooks


【解决方案1】:

我对您的代码进行了一些修复,请在 CodeSandbox 上查看。

这个想法是您不需要将列置于状态,而只需要使用选择框获取列。

我还通过实现 React.useMemo 来对其进行简单优化,以记忆计算的列。只有当 props.columnsselection 状态 发生变化时才会重新计算。

希望这会有所帮助!快乐编码! :)

【讨论】:

    【解决方案2】:

    好的,您的 tmpDataApp 传递到 DataTable 是只读的。因此,按照设计,您不会在此过程中看到任何数据更改。

    有几种方法可以让它工作,如果发生这种情况,主要是让您的DataTable 将更改传递回App

    第 1 步,您可以在 DataTable 上添加一个名为 onRowClickprop

      <DataTable data={tmpData} onRowClick={row => { console.log(row) } />
    

    第 2 步,您需要允许您的 tmpData 在活动结束后更改。你正在使用钩子,所以我们可以

      const [tmpData, setTmpData] = useState([sampleData])
      return (
        <DataTable data={tmpData} onRowClick={row => { 
          // use setTmpData to adjust the tmpData to get a new instance of tmpData
        } />
      )
    
    

    当然,对于这种复杂的事情,我们通常使用useReducer 而不是useState,因为除了选择行之外,您肯定还有其他事情要做:)

    【讨论】:

    • 但只有 DataTable 可以操作列进行过滤和选择。另外,我不需要像你说的那样改变 tmpData 。我想将所有选定的行推入一个数组调用selection
    • 在这种情况下,您需要将selectiononSelectionChange 传递给DataTable,我的朋友,有些东西必须通过接口。当然,如果您决定它们是DataTable 的内部变量,则不必传递它们。这就是为什么有时称为unControlled 组件的原因。
    猜你喜欢
    • 2020-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-16
    相关资源
    最近更新 更多