Javascript 可以处理setState的prevState参数吗';s函数是可变的吗?
我知道Javascript 可以处理setState的prevState参数吗';s函数是可变的吗?,javascript,reactjs,typescript,Javascript,Reactjs,Typescript,我知道这个。不应该直接修改状态,而应该使用设置状态 由此我推断,prevState也应该被视为不可变的,而setState应该总是这样: this.setState((prevState) => { // Create a new object instance to return const state = { ...prevState }; state.counter = state.counter + 1; return state; }); this.setSta
这个。不应该直接修改状态
,而应该使用设置状态
由此我推断,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) => {
prevState.flag = !prevState.flag;
return prevState;
});
或使用更深的嵌套:
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.setState((prevState) => {
// Create a new object instance to return
const state = { ...prevState };
state.counter = state.counter + 1;
return state;
});
this.setState((prevState) => {
prevState.flag = !prevState.flag;
return prevState;
});
我发现这是一个粗略的建议,所以我决定测试prevState
和this.state
的对象实例引用是否相同:
类测试扩展了React.Component{
状态={计数器:0};
onDemonstrateButtonClick=(事件)=>{
this.setState((prevState)=>{
如果(prevState==this.state)警报(`uh,yesp`);
prevState.counter++;
返回状态;
});
};
render(){
返回(
证明
{this.state.counter}
);
}
}
哇,他们是!那是哪一个呢?答案是错误的,我应该返回一个新的对象实例还是作为一个新对象返回部分更新,或者我可以随意地直接修改prevState
参数?如果是的话,这和直接变异这个.state有什么区别
旁注:TypeScript React键入不会将参数标记为只读
,这只会增加我的困惑。第一点
可以将setState函数的prevState参数视为
可变的
答案是不你永远不应该变异prevState
,这在react中也有明确提到
- prevState是对前一状态的引用。不应该这样 直接变异。相反,应该通过构建 基于prevState和props输入的新对象
prevState
和this.state
,它们是相同的,实际上它们不是
为了弄清楚为什么它们实际上不同,我们需要知道为什么prevState
实际上存在,答案是setState
函数是异步的,这就是react js允许我们访问prevState
的原因让我们检查下面的示例,其中prevState!=此.状态
在下面的示例中,我们将每次单击递增计数器两次,但我们将使用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>
)
}
}
上面的代码在此链接中完全正常工作,您可以检查console.log结果
如果您想在第二个
setState
从
到
您会发现结果不正确每次单击都会导致1个不正确的增量,因为我们有2个setState
,这是因为我们没有使用prevState
,我们使用了不正确的this.state
最后
我相信更新计数器的正确方法是
this.setState((prevState)=>({counter:prevState.counter+1}))
这是我第一次听说在使用设置状态
功能之前必须克隆整个状态对象。这对我来说有点抽象。无论如何-你不能改变它们的属性-例如,如果你有一个数组,你不能使用push
方法,因为它在原位工作并且会改变。这就是为什么建议使用spread语法
甚至concat
。整数也是如此i++
会变异,但i+1
不会。简短回答:也不要变异prevState。@TomášHübelbauer总结-this.setState((prevState)=>({counter:prevState.counter+1})代码>对我来说完全正确。但是,如果有比我更有经验的人能够批准或拒绝,我将不胜感激。“国家应该是不变的”这句话经常被无缘无故地重复。如果使用PureComponent
,则状态必须是不可变的,因为React将对不同的对象引用执行浅层比较检查。如果比较中的状态shouldComponentUpdate
,则不变性可能有助于进行有效的引用相等性检查。否则,请随意改变状态,因为React将在没有检查的情况下盲目地重新启动。不要改变。你链接的答案表明它会起作用,而不是说应该这样做。请注意他是如何在后面写下一个“更清晰”的选项的。感谢您提供的示例,prevState
没有引用与this.state
相同的对象。我抽象地知道这一点,但无法将其转化为代码,多亏了你,我现在感觉我真的理解了它。对于没有检查文档的明显失败,我也表示歉意,但现在我很高兴我没有这样做,因为我可能不会问这个问题,而且这个令人敬畏的答案也不存在。仅供参考,文档已被轻松更新,在更新器函数中不引用状态
,如prevState
:“状态是对应用更改时组件状态的引用。它不应该直接变异。”
return {
counter: prevState.counter + 1
}
return {
counter: this.state.counter + 1
}