React's setState method with prevState argument

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

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:

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

28 thoughts on “React's setState method with prevState argument”

  1. 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 you a snapshot(prevState) from the previous state. But if the change does not rely on any other previous value, then a shorter version is recommended this.setState({prop: newValue})

    this.setState(prevState =>{
       return{
            ...prevState,
            counter : prevState.counter +1
       }
    })
    
    this.setState({counter : 2})
    
    Reply
  2. 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.

    First, try to guess what should the result of Count when you click both buttons… If you think the count will be incremented by 4 on click of both buttons then it’s not right guess 😉

    Why? because in IncrementWithoutPrevState method since there are multiple setState calls, so React batched all those calls and updates the state only in the last call of setState in this method, so at the time of last call to setState in this method this.state.count is not yet updated and its value is still the same that was before entering into IncrementWithoutPrevState method so the resultant state will contain count incremented by 1.

    Now on the other hand side if we analyze the Increment method:
    Again there are multiple setState calls and React batched them all that means the actual state will be updated in the last call of setState but the prevState will always contain the modified state in the recent setState call. As previousState.count value has already been incremented 3 times till the last call of setState so the resultant state will contain the count value incremented by 4.

    Reply
  3. 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.

    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.

    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
    
    Reply
  4. 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

    Reply
  5. //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 }
        }
        increament() {
            this.setState({counter:this.state.counter+1})
        }
    
        increament3() {
            this.increament();
            this.increament()
            this.increament()
              
        }
       render() {
            return (
                <div>
                   count-{this.state.counter}
                    <div>
                        <button onClick={() => this.increament3()}>Increment</button>
                    </div>
                </div>
    
            )
        }
    }
    

    In this scenario when we click on Increment button then the output will be rendered into the UI is count-0 not count-3 because react groups all the state call in a single state call and does not carry the updated value over every incremented call. If I want to update the value based on the previous value then use below mentioned code.

    import React, { Component } from 'react'
    
    export default class Counter extends Component {
        constructor(props) {
            super(props);
            this.state = { counter: 0 }
        }
        increament() {
            this.setState((prevState=>({counter:prevState.counter+1})))
        }
    
        increamental() {
            this.increament();
            this.increament()
            this.increament()
         
        }
       render() {
            return (
                <div>
                   count-{this.state.counter}
                    <div>
                        <button onClick={() => this.increamental()}>Increament</button>
                    </div>
                </div>
    
            )
        }
    }
    

    In this scenario the output will be count-3 not count-0

    Reply

Leave a Comment