如何解决反应SetState覆盖整个对象而不是合并
我的状态在构造函数中如下所示:
this.state = {
selectedFile: null,//current file selected for upload.
appStatus: 'waiting for zip...',//status view
zipUploaded: false,zipUnpacked: false,capturingScreens: false,finishedCapture: false,htmlFiles: null,generatedList: [],optionValues: {
delayValue: 1
},sessionId: null,estimatedTime: null,zippedBackupFile: null,secondsElapsed:0,timer: {
screenshotStart:0,screenshotEnd:0,timingArray:[],averageTimePerUnit:25,totalEstimate:0
}
};
我的app.js中具有以下功能:
this.secondsCounter = setInterval(this.countSeconds,1000); // set inside the constructor
getStateCopy = () => Object.assign({},this.state);
countSeconds = () => {
let stateCopy = this.getStateCopy();
let currentSeconds = stateCopy.secondsElapsed + 1;
this.setState({secondsElapsed:currentSeconds});
}
captureTime = (startOrStop) => {
let stateCopy = this.getStateCopy();
let secondsCopy = stateCopy.secondsElapsed;
let startPoint;
if(startOrStop === true) {
this.setState({timer:{screenshotStart:secondsCopy}});
} else if(startOrStop === false){
this.setState({timer:{screenshotEnd:secondsCopy}});
startPoint = stateCopy.timer.screenshotStart;
stateCopy.timer.timingArray.push((secondsCopy-startPoint));
this.setState({secondsElapsed:secondsCopy})
stateCopy.timer.averageTimePerUnit = stateCopy.timer.timingArray.reduce((a,b) => a + b,0) / stateCopy.timer.timingArray.length;
this.setState({secondsElapsed:secondsCopy})
this.setState({timer:{averageTimePerUnit:stateCopy.timer.averageTimePerUnit}})
}
我收到一个错误,指出stateCopy.timer.timingArray上不存在“ push”。我进行了一些调查,发现this.setState({timer:{screenshotStart:secondsCopy}});实际上是覆盖状态中的整个“计时器”对象,并删除所有先前的属性,而不是合并它们。
我不明白我在做什么错..我正在使用stateCopy来避免对状态进行变异,并获取适当的值(避免异步混淆)。我在网上阅读的每一篇关于react的文章都表明,编写一个要声明状态的对象将与已经存在的对象合并,所以为什么它会不断覆盖“ timer”而不是合并?
解决方法
我做了一些调查,发现this.setState({timer:{screenshotStart:secondsCopy}});实际上是覆盖状态中的整个“计时器”对象,并删除所有先前的属性,而不是合并它们。
正确。 setState
仅在顶级处进行合并。低于此的任何东西都必须处理。例如:
this.setState(({timer}) => {timer: {...timer,screenshotStart: secondsCopy}});
请注意使用setState
的回调版本。重要的是,必须在任何时间内提供依赖于现有状态的状态信息。
在其他地方,您也必须执行相同的操作,包括当您推送到数组时。以下是一些其他说明:
没有理由在此处复制状态:
countSeconds = () => {
let stateCopy = this.getStateCopy();
let currentSeconds = stateCopy.secondsElapsed + 1;
this.setState({secondsElapsed: currentSeconds});
}
...并且(如上所述),您必须使用回调形式来基于现有状态可靠地修改状态。相反:
countSeconds = () => {
this.setState(({secondsElapsed}) => {secondsElapsed: secondsElapsed + 1});
};
类似地在captureTime
中:
captureTime = (startOrStop) => {
if (startOrStop) { // *** There's no reason for `=== true`
this.setState(({timer,secondsElapsed}) => {timer: {...timer,screenshotStart: secondsElapsed}});
} else { // *** Unless `startOrStop` may be missing or something,no need for `if` or `=== false`.
this.setState(({timer,secondsElapsed}) => {
const timingArray = [...timer.timingArray,secondsElapsed - timer.screenshotStart];
const update = {
timer: {
...timer,screenshotEnd: secondsElapsed,timingArray,averageTimePerUnit: timingArray.reduce((a,b) => a + b,0)
}
};
});
}
};
旁注:您的copyState
函数执行浅状态复制。因此,如果您修改其包含的对象的任何属性,那么您将直接修改状态,而在React中则不必这样做。
setState挂钩总是用一个新对象覆盖状态……这是它们的正确行为。
您需要在setState中使用一个函数。不只是传递一个对象。
setState((prevState,prevProps)=>{
//logic to make a new object that you will return ... copy properties from prevState as needed.
//something like const newState = {...prevState} //iffy myself on exact syntax
return newState
})
,
您的getStateCopy
仅克隆现有状态-嵌套的任何内容都不会克隆。为了说明:
const getStateCopy = () => Object.assign({},state);
const state = {
foo: 'bar',arr: [1,2]
};
const shallowCopy = getStateCopy();
shallowCopy.foo = 'newFoo';
shallowCopy.arr.push(3);
console.log(state);
或者先深度克隆状态,或者使用spread添加所需的新属性:
countSeconds = () => {
this.setState({
...this.state,secondsElapsed: this.state.secondsElapsed + 1
});
}
captureTime = (startOrStop) => {
if (startOrStop === true) {
this.setState({ ...this.state,timer: { ...this.timer,screenshotStart: this.state.secondsElapsed } });
} else if (startOrStop === false) {
const newTimingValue = this.state.secondsElapsed - this.state.timer.screenshotStart;
const newTimingArray = [...this.state.timer.timingArray,newTimingValue];
this.setState({
...this.state,timer: {
...this.timer,screenshotEnd: this.state.secondsElapsed,timingArray: newTimingArray,averageTimePerUnit: newTimingArray.reduce((a,0) / newTimingArray.length,},});
}
}
如果始终使用captureTime
或true
调用false
,则可以使用以下方法使事情看起来更简洁:
captureTime = (startOrStop) => {
if (startOrStop) {
this.setState({ ...this.state,screenshotStart: this.state.secondsElapsed } });
return;
}
const newTimingValue = this.state.secondsElapsed - this.state.timer.screenshotStart;
const newTimingArray = [...this.state.timer.timingArray,newTimingValue];
// etc
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。