使用prevState参数的React的setState方法

时间:2019-04-03 12:27:26

标签: javascript reactjs

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

class MyApp extends React.Component {

  state = {
    count: 3
  };

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

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

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

6 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:1)

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

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

this.setState({counter : 2})

答案 2 :(得分:0)

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>
    );
  }
}

我只是为您提供了一个示例,以说明“ React可能批处理多个setState()...”的含义,以及在上述情况下为什么应使用prevState的原因。

首先,尝试猜测 ,当您同时单击两个按钮时,Count的结果应该是什么...如果您认为count将在两个按钮上单击时都增加4,则不正确;)

为什么?因为在IncrementWithoutPrevState方法中 ,因为存在多个setState调用,所以React批量处理了所有这些调用并仅在{{1 }},因此在此方法上一次调用state时,setState尚未更新,其值仍与进入setState方法之前的值相同,因此结果状态将包含递增1的this.state.count

另一方面,如果我们分析IncrementWithoutPrevState方法,则

现在: 再次有多个count调用,React将它们分批处理,这意味着实际状态将在上一次调用Increment中更新,但是setState将始终包含修改后的setState在最近的prevState通话中。由于state的值已经增加了3次,直到最后一次调用setState,所以结果previousState.count将包含递增4的计数值。

答案 3 :(得分:0)

如果您多次调用一个函数以在单个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>
        )
    }

最后sec的值将为1,因为调用是异步的,与c ++ / c#或java等高级编程语言不同,在Java中,总是有一个主函数来维护主线程。 因此,如果要使fiveSecJump()函数正常工作,则必须通过向其传递箭头函数来提供帮助。 prevState。 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

答案 4 :(得分:0)

//Here is the example to explain both concepts:


import React, { Component } from 'react'

export default class Counter extends Component {
    constructor(props) {
        super(props);
        this.state = { counter: 0 }
    }

    increment() {
        this.setState({counter:this.state.counter+1})
    }

    increment3() {
        this.increment();
        this.increment()
        this.increment()
    }
   render() {
        return (
            <div>
               count-{this.state.counter}
                <div>
                    <button onClick={() => this.increment3()}>Increment</button>
                </div>
            </div>

        )
    }
}

在这种情况下,当我们点击 Increment 按钮时,输出将呈现到 UI 中的是 count-0 而不是 count-3因为 react 将所有状态调用组合在一个状态调用中​​,并且不会在每次递增的调用中都携带更新的值。如果我想根据以前的值更新值,请使用下面提到的代码。

import React, { Component } from 'react'

export default class Counter extends Component {
    constructor(props) {
        super(props);
        this.state = { counter: 0 }
    }

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

    incremental() {
        this.increment();
        this.increment()
        this.increment()
     
    }
   render() {
        return (
            <div>
               count-{this.state.counter}
                <div>
                    <button onClick={() => this.incremental()}>Increment</button>
                </div>
            </div>

        )
    }
}

在这种情况下,输出将是 count-3 而不是 count-0

答案 5 :(得分:0)

@samee 的回答很棒,帮助了我。我想重温这个话题来解决 React 函数组件,只是因为这是我目前正在研究的内容。

以下示例说明了为什么如果新状态是使用先前状态计算的,您可能希望使用 prevState

function App() {
  const [developer, setDeveloper] = useState({
    yearsExp: 0,
  });

  function handleYearsExp() {
    setDeveloper((prevState) => ({
      yearsExp: prevState.yearsExp + 1, //increments by 1
    }));
    setDeveloper((prevState) => ({
      yearsExp: prevState.yearsExp + 1, //increments by another 1
    }));
  }

  return (
    <>
      <button onClick={handleYearsExp}>Increment Years</button> {/* will increment by 2 */}
      <p>
        I am learning ReactJS and I have {developer.yearsExp} years experience
      </p>
    </>
  );
}

这是没有将 prevState 函数传递给 setState 的代码。请注意,第一个 setState 函数基本上被忽略了。

function App() {
  const [developer, setDeveloper] = useState({
    yearsExp: 0,
  });

  function handleYearsExp() {
    setDeveloper({
      yearsExp: developer.yearsExp + 1, // is ignored
    });
    setDeveloper({
      yearsExp: developer.yearsExp + 1, //increments by 1
    });
  }

  return (
    <>
      <button onClick={handleYearsExp}>Increment Years</button> {/* will only increment by 1 */}
      <p>
        I am learning ReactJS and I have {developer.yearsExp} years experience
      </p>
    </>
  );
}

参考文献:

  1. ReactJS.org - "Pass a function to setState"
  2. ReactJS.org - "React may batch multiple setState() calls into a single update for performance.