0

I usually update state in react to toggle a icon display in react:

  toggle = () => {
    this.setState({
       open: !this.state.open
    })  // setState
  } // toggle callback

Now I saw a new way of doing it, which is recommended over the above way:

  toggle = () => {
    this.setState((prevState, props) => {
      return {
        open: !prevState.open
      } // return
    })  // setState
  } // toggle callback

In this case, the setState function consumes a updater(which in this case is a callback function), which worked. how does the setState function consumes the updater? The second parameter in updater props was not even used, what is the use of it?

  • `.setState()` detects whether you passed an object or function, and if the latter, calls the function and uses what it returns as new state. –  Sep 07 '17 at 08:42
  • How does the prevState and props gets their value? I guess they get it from this.setState. But how does it do that? – Xiaojun Yu Sep 07 '17 at 08:49
  • Here's example code: https://jsfiddle.net/br1sm4zj/ If you want to see the exact implementation, just look at the Component base class. The confusion here might arise from the fact that you're writing a callback, because usually the library provides the functions and *you* call them. However when using callbacks, you're providing a function which is then called by the framework (React). It can take a while to wrap your head around it. –  Sep 07 '17 at 09:00

2 Answers2

2

In your case, you may omit the second parameter to the setState,

toggle = () => {
    this.setState((prevState) => {
      return {
        open: !prevState.open
      } 
    })  
  }

However, its useful when you want to update the state based on both the current props and the prevState value.

Also

(prevState) => {
      return {
        open: !prevState.open
      } 
    }

is not a callback, but an updater function. According to the React docs:

The first argument is an updater function with the signature:

(prevState, props) => stateChange

prevState is a reference to the previous state. It should not be directly mutated. Instead, changes should be represented by building a new object based on the input from prevState and props. For instance, suppose we wanted to increment a value in state by props.step:

this.setState((prevState, props) => {
  return {counter: prevState.counter + props.step};
});

Both prevState and props received by the updater function are guaranteed to be up-to-date. The output of the updater is shallowly merged with prevState.

Also refer to this answer on Stackoverflow to see what a setState callback is useful for,

When to use React setState callback

Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • +1, but I would add that you would want to use this syntax if you are updating the state based off the previous state. For example, incrementing the counter by 1. Otherwise, one can use the other method of passing in an object. – Chris Sep 07 '17 at 09:01
  • The updater receives the prevState and props(previous state and current state). So I guess setState is a promise which make use of updater. Right? – Xiaojun Yu Sep 07 '17 at 09:09
  • @XiaojunYu, the React docs give a very good idea on how setState behaves, It basically enqueues the actions to be taken. I would recommend you to read this https://facebook.github.io/react/docs/react-component.html#setstate – Shubham Khatri Sep 07 '17 at 09:15
  • I updated the answer to add a link which provides a description of when should you use the setState callback – Shubham Khatri Sep 07 '17 at 09:17
  • I think the updater function is also a callback function, since it's passed into the setState function as a parameter. It's just not 'the' callback (which is the 2nd parameter in the setState function). Please correct me if I'm wrong. – Toffee Conmigo Nov 06 '20 at 07:42
0

I want to add more information why using a callback function in setState should be recommended.

setState  is asynchronous

React batches state changes for performance, so that your state may not change immediately after setState. That means you should not rely on the current state when calling setState because you cannot be sure what that state will be!

Moreover, your syntax could be simply

this.setState(prevState => ({ open: !prevState.open }))

Chris
  • 57,622
  • 19
  • 111
  • 137
An Nguyen
  • 1,487
  • 10
  • 21