Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/399.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 可以处理setState的prevState参数吗';s函数是可变的吗?_Javascript_Reactjs_Typescript - Fatal编程技术网

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
}