javascript React 的 setState 方法与 prevState 参数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/55495198/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 10:33:13  来源:igfitidea点击:

React's setState method with prevState argument

javascriptreactjs

提问by slowjams

I'm new to React, just have a question on setState method. Let's say we have a component:

我是 React 的新手,只是有一个关于 setState 方法的问题。假设我们有一个组件:

class MyApp extends React.Component {

  state = {
    count: 3
  };

  Increment = () => {
    this.setState((prevState) => ({
      options: prevState.count + 1)
    }));
  }
}

so why we have to use prevState in the setState method? why can't we just do:

那么为什么我们必须在 setState 方法中使用 prevState 呢?为什么我们不能这样做:

this.setState(() => ({
  options: this.state.count + 1)
}));

回答by Dupocas

Both signatures can be used, the only difference is that if you need to change your state based on the previous state you should use this.setState(function)which will provide for you a snapshot(prevState) from the previous state. But if the change does not rely on any other previous value, than a shorter version is recommended this.setState({prop: newValue})

两个签名都可以使用,唯一的区别是如果你需要根据之前的状态改变你的状态,你应该使用this.setState(function)它会为你提供一个prevState来自之前状态的快照()。但如果更改不依赖于任何其他先前的值,则建议使用较短的版本this.setState({prop: newValue})

this.setState(prevState =>{
   return{
        ...prevState,
        counter : prevState.counter +1
   }
})

this.setState({counter : 2})

回答by samee

class MyApp extends React.Component {

  state = {
    count: 0
  };

  Increment = () => {
    this.setState(prevState => ({
      count: prevState.count + 1
    }));
    this.setState(prevState => ({
      count: prevState.count + 1
    }));
    this.setState(prevState => ({
      count: prevState.count + 1
    }));
    this.setState(prevState => ({
      count: prevState.count + 1
    }));
  };

  IncrementWithoutPrevState = () => {
    this.setState(() => ({
      count: this.state.count + 1
    }));
    this.setState(() => ({
      count: this.state.count + 1
    }));
    this.setState(() => ({
      count: this.state.count + 1
    }));
    this.setState(() => ({
      count: this.state.count + 1
    }));
  };

  render() {
    return (
      <div>
        <button onClick={this.IncrementWithoutPrevState}>
          Increment 4 times without PrevState
        </button>
        <button onClick={this.Increment}>
          Increment 4 times with PrevState
        </button>
        <h1>Count: {this.state.count}</h1>
      </div>
    );
  }
}

I just made an example for you to give an idea what is meant by "React may batch multiple setState()..." and why we should use prevState in the above scenario.

我只是举了一个例子让你了解“React 可以批处理多个 setState()...”是什么意思,以及为什么我们应该在上面的场景中使用 prevState。

First, try to guesswhat should the result of Countwhen you click both buttons... If you think the countwill be incremented by 4 on click of both buttons then it's not right guess ;)

首先,试着猜一下Count当你点击两个按钮时会产生什么结果......如果你认为点击两个按钮count都会增加 4,那么这不是正确的猜测;)

Why? becausein IncrementWithoutPrevStatemethod since there are multiple setStatecalls, so React batched all those calls and updates the stateonly in the last call of setStatein this method, so at the time of last call to setStatein this method this.state.countis not yet updated and its value is still the same that was before entering into IncrementWithoutPrevStatemethod so the resultant state will contain countincremented by 1.

为什么?因为IncrementWithoutPrevState方法,因为有多个setState电话,等反应过来批量所有这些电话,并更新state只在最后调用setState这个方法,所以在最后一次通话的时间,setState在这个方法this.state.count还没有被更新,其价值仍然是相同的那是在进入IncrementWithoutPrevState方法之前,因此结果状态将包含count递增 1。

Now on the other hand side if we analyze the Incrementmethod:Again there are multiple setStatecalls and React batched them all that means the actual state will be updated in the last call of setStatebut the prevStatewill always contain the modified statein the recent setStatecall. As previousState.countvalue has already been incremented 3 times till the last call of setStateso the resultant statewill contain the count value incremented by 4.

现在另一方面,如果我们分析该Increment方法:再次有多个setState调用并且 React 将它们全部批处理,这意味着实际状态将在最后一次调用中更新,setStateprevState将始终包含state在最近setState调用中修改的状态。由于previousState.count值已经增加了 3 次,直到最后一次调用,setState因此结果state将包含增加了 4 的计数值。

回答by Lord

If you call a function multiple times to change the value of a state property in a single render()function call then the updated value will not go over between the different calls without prevState mechanism.

如果在单个render()函数调用中多次调用一个函数来更改状态属性的值,则更新的值将不会在没有 prevState 机制的不同调用之间传递。

second(){
   this.setState({ // no previous or latest old state passed 
       sec:this.state.sec + 1
     }, ()=>{
        console.log("Callback value", this.state.sec)
   })    
}

fiveSecJump(){ // all this 5 call will call together  
       this.second() // this call found sec = 0 then it will update sec = 0 +1
       this.second() // this call found sec = 0 then it will update sec = 0 +1
       this.second() // this call found sec = 0 then it will update sec = 0 +1
       this.second() // this call found sec = 0 then it will update sec = 0 +1
       this.second() // this call found sec = 0 then it will update sec = 0 +1
   }

  render() {
        return (
            <div>
              <div>  Count - {this.state.sec}</div>
              <button onClick ={()=>this.fiveSecJump()}>Increment</button>
            </div>
        )
    }

Finally sec value will be 1, because calls are async, not like high-level programming language like c++/c# or java where there is always a main function which maintains the main thread.
Therefore if you want to have fiveSecJump()function to work properly you have to help it by passing it an arrow function. prevState. prevState is not a keyword or member function, you can write any word here like oldState, stackTopState, lastState. It will convert to a generic function which will do your desired work.

最后 sec 值将是 1,因为调用是异步的,不像 c++/c# 或 java 这样的高级编程语言总是有一个维护主线程的 main 函数。
因此,如果你想让fiveSecJump()函数正常工作,你必须通过向它传递一个箭头函数来帮助它。上一个状态。prevState 不是关键字或成员函数,你可以在这里写任何词,如 oldState、stackTopState、lastState。它将转换为通用函数,可以完成您想要的工作。

class Counter extends Component {
   constructor(props){
        super(props)
        this.state = {
            sec:0
        }
   }
   second(){
        this.setState(prevState =>({
            sec:prevState.sec + 1
        }))
   }

   fiveSecJump(){
       this.second() // this call found sec = 0 then it will update sec = 0 +1
       this.second() // this call found sec = 1 then it will update sec = 1 +1
       this.second() // this call found sec = 2 then it will update sec = 2 +1
       this.second() // this call found sec = 3 then it will update sec = 3 +1
       this.second() // this call found sec = 4 then it will update sec = 4 +1

   }
    render() {
        return (
            <div>
              <div>  Count - {this.state.sec}</div>
              <button onClick ={()=>this.fiveSecJump()}>Increment</button>
            </div>
        )
    }
}

export default Counter

回答by arslan

here iseveryone know just state:prevstate.counter+1.. we can also do this with state:this.state.counter+1. This is not the way i think to use prevstate. i have problem in array when i push into existing state it allow me but second time it prevent changes

这里是每个人都知道 state:prevstate.counter+1.. 我们也可以用 state:this.state.counter+1 来做到这一点。这不是我认为使用 prevstate 的方式。当我进入现有状态时,我在数组中遇到问题它允许我但第二次它阻止更改