我知道this.state
不应该直接修改,而应该使用setState
。
据此我推断prevState
也应被视为不可变的,而setState
应该始终看起来像这样:
this.setState((prevState) => {
// Create a new object instance to return
const state = { ...prevState };
state.counter = state.counter + 1;
return state;
});
或更深层的嵌套:
this.setState((prevState) => {
// Create a new object instance to return
const state = { ...prevState, counter: { ...prevState.counter } };
state.counter.value = state.counter.value + 1;
return state;
});
或只是像
setState({})
这样的部分更新,在其中更容易使用。this.setState((prevState) => ({ counter: prevState.counter + 1 }));
上面所有这些显然都是正确的,因为它们返回了一个新实例,但是随后我遇到了this question where the accepted answer encourages mutating
prevState
without returning a new object instance(注意问题中的代码块)。像这样的东西:
this.setState((prevState) => {
prevState.flag = !prevState.flag;
return prevState;
});
我发现这是一个粗略的建议,因此我决定测试
prevState
和this.state
的对象实例引用是否相同:(The JSFiddle)
class Test extends React.Component {
state = { counter: 0 };
onDemonstrateButtonClick = (event) => {
this.setState((prevState) => {
if (prevState === this.state) alert(`uh, yep`);
prevState.counter++;
return prevState;
});
};
render() {
return (
<div>
<button onClick={this.onDemonstrateButtonClick}>Demonstrate</button>
{this.state.counter}
</div>
);
}
}
Whadayaknow,他们是!那是什么呢?答案是否正确,我应该返回一个新的对象实例还是将部分更新作为一个新对象返回,还是可以疯狂地直接更改
prevState
参数?如果是的话,这与直接更改this.state
有何不同?旁注:TypeScript React输入不会将参数标记为
ReadOnly
,这只会增加我的困惑。 最佳答案
第一点
答案是否,您不应该突变prevState
,在react documentation in setState section中也明确提到
第二点:
您测试了
prevState
和this.state
,它们是相同的,实际上它们不是相同的。为了弄清楚为什么它们实际上不同,我们需要知道为什么
prevState
实际上存在,答案是setState
函数是异步的,这就是为什么react js允许我们访问prevState
的原因,请检查下面的示例,其中prevState != this.state
在下面的示例中,每次单击将使计数器增加两次,但是我们将使用2个
setState
操作,其中每个操作都会使计数器增加1。由于
setState
是async
,因此您会注意到第二个setState
操作在第一个setState
完成之前开始,这是prevState
有用的地方,此处prevState
和this.state
不相等。我在每行注释了一个数字,表示执行该行的时间,这应该解释为什么我们需要
prevState
以及为什么它与this.state
不同。class App extends React.Component{
constructor(props)
{
super(props);
this.state = {
counter : 1
};
}
increment = () =>{
this.setState((prevState , props) => {
console.log("in first"); //3
console.log(prevState); //3
console.log(this.state); //3
if(prevState == this.state)
{
console.log("in first prevState == this.state");
}
return {
counter : prevState.counter+1
}
} , ()=>{
console.log("done updating first"); //5
});
console.log("after first"); //1
this.setState((prevState, props) => {
console.log("in second"); //4
console.log(this.state); //4
console.log(prevState); //4
if (prevState == this.state) {
console.log("in second prevState == this.state");
}
return {
counter: prevState.counter + 1
}
} , () =>{
console.log("done updating second"); //6
});
console.log("after second"); //2
}
render(){
return (
<div>
<span>{this.state.counter}</span>
<br/>
<button onClick={this.increment} >increment</button>
</div>
)
}
}
上面代码的结果是
"after first"
"after second"
"in first"
▶Object {counter: 1}
▶Object {counter: 1}
"in first prevState == this.state"
"in second"
▶Object {counter: 1}
▶Object {counter: 2}
"done updating first"
"done updating second"
上面的代码在此链接中完全可用,您可以检查console.log结果
https://codesandbox.io/s/k325l485mr
上面的示例将正确地使计数器每次点击递增两次,如果您想使其中断,请在第二个
setState
中更改change语句从
return {
counter: prevState.counter + 1
}
至
return {
counter: this.state.counter + 1
}
并且您会发现结果不正确,因为每次单击都会导致1的增量不正确,因为我们有2个
setState
,这是因为我们没有使用prevState
,并且使用了错误的this.state
最后
我认为更新计数器的正确方法是
this.setState((prevState) => ({ counter: prevState.counter + 1 }));
关于javascript - 是否可以将setState函数的prevState参数视为可变的?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/47339643/