我有一些似乎无法正常工作的代码。我有一个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-始终首先执行所有逻辑,然后提交状态更改。