有没有比forceUpdate()更好的解决方案

时间:2019-06-28 10:33:57

标签: javascript reactjs

按我的组件状态保存的数据(片段)如下:

[
    {
        batchId: 1234,
        ...
        jobRowData: [
            {
                assignmentId: 12345,
                isSelected: false,
                ...
            },
            {
                assignmentId: 12346,
                isSelected: false,
                ...
            }
        ]
    },
    {
        batchId: 1235,
        ...
        jobRowData: [
            {
                assignmentId: 12347,
                isSelected: false,
                ...
            },
            {
                assignmentId: 12348,
                isSelected: false,
                ...
            }
        ]
    }
]

我要更改isSelected,并使用以下方法进行操作:

handleIsSelectedChangedJob(selectedIdsData){
    let jobRowData = this.state.batchRowData.find((x) => {return x.batchId === selectedIdsData.batchId}).jobRowData
    let jobRowItemData = jobRowData.find((x) => {return x.assignmentId === selectedIdsData.assignmentId})
    jobRowItemData.isSelected = !jobRowItemData.isSelected // do the update
    // this.setState({batchRowData: [...this.state.batchRowData]}) // this also works instead of this.forceUpdate()
    this.forceUpdate() // how to refresh view according to update 2 lines above ?
}

其中的参数selectedIdsData可能如下所示:

{assignmentId: 12346, batchId: 1234}

我的问题是:除了执行this.forceUpdate()之外,还有其他刷新视图的方法吗?上面的带注释的行也可以完成这项工作,但是它对整个状态对象起作用,而不是对一个更改的属性起作用。

2 个答案:

答案 0 :(得分:1)

绝对!您永远不要直接(通过引用)修改状态,而这正是handleIsSelectedChangedJob内部发生的事情。您必须创建一个新状态并调用setState

handleIsSelectedChangedJob({assignmentId, batchId}) {
  const dataIndex = this.state.batchRowData.findIndex(x => x.batchId === batchId);
  const data = this.state.batchRowData[dataIndex];
  const itemIndex = data.jobRowData.findIndex(x => x.assignmentId === assignmentId);
  const item = data.jobRowData[itemIndex];

  const newItem = {...item, isSelected: !item.isSelected};
  const newData = {
    ...data,
    jobRowData: [].concat(
      data.jobRowData.slice(0, itemIndex),
      newItem,
      data.jobRowData.slice(1 + itemIndex)
    )
  };

  this.setState({
    batchRowData: [].concat(
      batchRowData.slice(0, dataIndex),
      newData,
      batchRowData.slice(1 + dataIndex)
    )
  });
}

如您所见,它的代码更多,但是现在它并没有改变状态,只复制了所需的内容。当然,有很多库可以做到这一点。它仍然不是完美的,因为阅读this.state然后调用this.setState并不好-使用this.state(state => diff)会更安全:

handleIsSelectedChangedJob({assignmentId, batchId}) {
  this.setState(state => {
    const dataIndex = state.batchRowData.findIndex(x => x.batchId === batchId);
    const data = state.batchRowData[dataIndex];

    // Same as above...

    return {
      batchRowData: [].concat(
        batchRowData.slice(0, dataIndex),
        newData,
        batchRowData.slice(1 + dataIndex)
      )
    };
  });
}

答案 1 :(得分:1)

这应该可以让您做自己想要做的事情:

handleIsSelectedChangedJob(selectedIdsData){
    const _batchRowData = ...this.state.batchRowData;
    const batchIndex = _batchRowData.findIndex((x) => x.batchId === selectedIdsData.batchId);
    const jobIndex = batchIndex < 0 ? -1 : _batchRowData[batchIndex].jobRowData.findIndex((x) => x.assignmentId === selectedIdsData.assignmentId);

    if (batchIndex < 0 || jobIndex <  0) {
        return;
    }

    _batchRowData[batchIndex].jobRowData[jobIndex].isSelected = !batchRowData[batchIndex].jobRowData[jobIndex].isSelected;

    this.setState({ batchRowData: [..._batchRowData] });
}