我有一些似乎无法正常工作的代码。我有一个onChange处理程序,它向其父组件的setState()发送键和值。

_onAttributeChange(key, value) {
    const changes = this.state.changes;
    const pushObj = {key: key, value: value};
    console.log(pushObj);
    if (changes.length === 0) {
        this.setState({changes: changes.concat([pushObj])});
    } else {
        changes.forEach(change => {
            if (change.key === key) {
                console.log('Update');
            } else {
                console.log('No update');
                this.setState({changes: changes.concat([pushObj])});
            }
        });
    }
 }


从此代码中您可以看到,我正在基于键值参数(看起来像{key: 1, value: 'Some Value'})创建对象。目的是在数组长度为0的情况下将其连接到数组。该部分可以正常工作。 forEach是我遇到问题的地方。我的目标是遍历状态数组,检查进入的change.key是否与数组中的键匹配,如果确实要执行更新,否则我想再次确认以将更改添加到数组中。

所以有两个问题:


首先,当第一个更改进入时,循环正常工作,并且只需使用相同的键(即1)对每个连续的更改进行控制台日志Update。当另一个更改进入时,它首先可以正常工作并将其连接到阵列,但是随后使用键2进行的任何连续更改都将触发Update AND else子句的控制台日志。
其次,如何在遍历数组时更新更改,而又不直接改变状态?

最佳答案

答案确实应该很简单。只需处理状态的临时副本,完成后就用副本替换实际状态。

_onAttributeChange(key, value) {
  let changes = this.state.changes.slice();
  const pushObj = {key: key, value: value};
  console.log(pushObj);
  if (changes.length === 0) {
    changes = changes.concat([pushObj]);
  } else {
    changes.forEach(change => {
      if (change.key === key) {
        console.log('Update');
      } else {
        console.log('No update');
        changes = changes.concat([pushObj]);
      }
    });
  }
  this.setState({changes: changes});
}


您在进行连续更改时遇到的问题很可能与setState()的异步性质有关。这样的调用不仅会导致不必要的重新渲染,而且几乎可以肯定的是,它还会以不需要的方式更新您的状态。

看一下官方的React文档对setState()的看法:


  setState()不会立即变异this.state而是创建一个
  等待状态转换。调用此后访问this.state
  方法可能会返回现有值。没有
  保证对setState的调用的同步操作,并且调用可以
  分批进行性能提升。


因此,从根本上讲,当您实际上希望从循环中的上一个迭代中获取更新的状态值时,很可能会返回“旧”状态值。



这里要注意的另一个非常重要的事情是,您需要创建正在处理的state属性的副本。否则,您将直接更改状态,这可能会起作用,但不鼓励这样做。

所以基本上总是这样做:

//To copy an array
let arr = this.state.myArray;  //wrong. arr still references myArray
let arr = this.state.myArray.slice();  //correct

//To copy an object
let obj = this.state.myObject;  //wrong. obj still references myObject
let obj = Object.assign({}, this.state.myObject);  //correct


有关slice()Object.assign()的MDN的更多信息。

另外,请谨慎使用const。仅当变量必须是不可变的时才使用它。



TL; DR-始终首先执行所有逻辑,然后提交状态更改。

09-17 15:53
查看更多