React setState(

SetState () is the React API recommended for updating component states, but do you really know setState()? Let me talk about it in detail.

SetState () official usage guide

Syntax 1: setState(updater[, callback])

  • Updater: function type that returns an updated state object that is shallow merged with state.

  • Callback: Optional, callback function.

Syntax 2: setState(stateChange[, callback])

  • SetState: Object type that superficially merges incoming objects into the new state.

  • Callback: Optional, callback function.

You can either select a function to return a new state object, or you can directly select an object to apply to state updates. When do you select parameters of the function type and when do you select parameters of the object type? Two words can be summed up here:

  • Select the object type parameter when the current state update does not depend on the previous state

  • Select the function type parameter when the current update state depends on the previous state

Example:


      
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>SetState,</title>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/[email protected]/babel.min.js"></script>
</head>
<body>
  <div id="app"></div>
  <script type="text/babel">
    class A extends React.Component {
      state = {
        count: 0
      }
      update1 = (a)= > {
        this.setState({count: this.state.count+1})
      }

      update2 = (a)= > {
        this.setState(state= > ({
          count: state.count+1
        }))
      }

      update3 = (a)= > {
        this.setState({
          count: 8
        })
      }  

      render () {
        return (
          <div>
            <h1>{this.state.count}</h1>
            <button onClick={this.update1} style={{marginRight: 15}} >Test 1</button><button style={{marginRight: 15}} onClick={this.update2}>Test 2</button><button onClick={this.update3}>Test 3</button>
          </div>         
        )
      }
    }
    ReactDOM.render(
    <A/>.document.getElementById('app'))</script>
</body>
</html>
Copy the code

In this example, we change the count state of component A by clicking on the button to test 1 or test 2. Since each change in the state is an increment of 1, it is appropriate to select the function type parameter in setState, which is recommended as update2.

Clicking the Test 3 button directly changes the count value to a fixed value of 8. This does not depend on the previous count state value, so it is appropriate to select the object type parameter in setState, which is recommended as update3.

Is setState() updating state necessarily asynchronous?

We know that setState() triggers the component render() function, which rerenders the component to display the updated content on the view, so can we get the latest state immediately after setState()?

Is setState() updated asynchronously or synchronously?

Conclusion:

  • SetState () in the React related callback function is an asynchronous update

  • SetState () is not synchronized in React related callbacks

React related callbacks include: component lifecycle hooks, React component event listener callbacks.

React unrelated callbacks include the common ones: setTimeout(), Promise(), and so on.

Again, we can test this with our previous button click example.

Example:


      
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>SetState,</title>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/[email protected]/babel.min.js"></script>
</head>
<body>
  <div id="app"></div>
  <script type="text/babel">
    class A extends React.Component {
      state = {
        count: 0
      }
      update1 = (a)= > {
        this.setState({count: this.state.count+1})
        console.log(this.state.count)
      }

      update2 = (a)= > {
        setTimeout((a)= > {
          this.setState(state= > ({
            count: state.count+1
          }))
          console.log(this.state.count)
        })
      }

      update3 = (a)= > {
        Promise.resolve().then(value= > {
          this.setState({
            count: 8
          })
          console.log(this.state.count)
        })
      }

      componentWillMount () {
        this.setState(state= > ({
          count: state.count+1
        }))
        console.log(this.state.count)
      }

      render () {
        console.log('render()'.this.state.count)
        return (
          <div>
          <h1>{this.state.count}</h1>
            <button onClick={this.update1} style={{marginRight: 15}} >Test 1</button><button style={{marginRight: 15}} onClick={this.update2}>Test 2</button><button onClick={this.update3}>Test 3</button>
          </div>         
        )
      }
    }
    ReactDOM.render(
    <A/>.document.getElementById('app'))</script>
</body>
</html>
Copy the code

The React callback (update1) and componentWillMount() hooks print the latest state value after setState(). But the page has been updated to its current status.

Using the same method, we can observe that setTimeout() and Promise() callbacks to update2 print the latest state value after setState(). And this print happens after setState() triggers the component to rerender (). Test results confirm that our conclusion is correct. SetState () is the asynchronous update state in React related callbacks, and setState() is the synchronous update state in unrelated callbacks.

How do I get the latest state value when setState() updates the state asynchronously?

SetState () = setState() = setState(); setState() = setState();

The answer is simple: the second callback() argument to the setState() argument. The second callback to setState() is called after updating the state and after the component rerenders (), where we get the latest state value.

Code:

. update1 =(a)= > {
  this.setState({count: this.state.count+1= > {}, ()console.log(this.state.count)
  })
}

componentWillMount () {
  this.setState(state= > ({
    count: state.count+1= > {}), ()console.log(this.state.count)
  })
}
Copy the code

This way, we can also print the latest state value in Update1 and componentWillMount().

How to React when setState() is called repeatedly?

The premise of our discussion here is that setState() updates the state asynchronously, because of synchronous update, we call setState() several times, will trigger several render hooks, of course, will print the updated state value in real time.

Conclusion:

Here are two cases to discuss:

  • When setState() passes an object type argument, React will combine multiple calls to setState() to trigger render once.

  • When setState() passes function type arguments, React calls setState() multiple times, triggering render once.

As you can see, we call setState() multiple times, regardless of the type of argument passed. React only calls Render once to re-render the component.

We can also test our conclusions with a button click example.

Example:


      
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>SetState,</title>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/[email protected]/babel.min.js"></script>
</head>
<body>
  <div id="app"></div>
  <script type="text/babel">
    class A extends React.Component {
      state = {
        count: 0
      }
      update1 = (a)= > {
        // this.setState({count: this.state.count+1}, () => {
        // console.log(this.state.count)
        // })
        // this.setState({count: this.state.count+1}, () => {
        // console.log(this.state.count)
        // })
        // this.setState({count: this.state.count+1}, () => {
        // console.log(this.state.count)
        // })
        this.setState((state) = > ({
          count: state.count+1= > {}), ()console.log(this.state.count)
        })
        this.setState((state) = > ({
          count: state.count+1= > {}), ()console.log(this.state.count)
        })
        this.setState((state) = > ({
          count: state.count+1= > {}), ()console.log(this.state.count)
        })
      }

      update2 = (a)= > {
        setTimeout((a)= > {
          this.setState(state= > ({
            count: state.count+1
          }))
          console.log(this.state.count)
          this.setState(state= > ({
            count: state.count+1
          }))
          console.log(this.state.count)
          this.setState(state= > ({
            count: state.count+1
          }))
          console.log(this.state.count)
        })
      }

      update3 = (a)= > {
        Promise.resolve().then(value= > {
          this.setState({
            count: 8
          })
          console.log(this.state.count)
        })
      }

      componentWillMount () {
        this.setState(state= > ({
          count: state.count+1
        }))
        console.log(this.state.count)
      }

      render () {
        console.log('render()'.this.state.count)
        return (
          <div>
          <h1>{this.state.count}</h1>
            <button onClick={this.update1} style={{marginRight: 15}} >Test 1</button><button style={{marginRight: 15}} onClick={this.update2}>Test 2</button><button onClick={this.update3}>Test 3</button>
          </div>         
        )
      }
    }
    ReactDOM.render(
    <A/>.document.getElementById('app'))</script>
</body>
</html>
Copy the code

When you click test button 2, because setState() updates the state synchronously, you can see that the component makes multiple render calls, printing out the updated state values in sequence, which is easy.

We click test button 1 to test different setState() parameters. We find that when the parameter is an object type, React merges the setState() call repeatedly, that is, only updates the state once. When the function type parameter is passed, calculation updates are performed separately.

React only makes one render call, regardless of which way setState() is called repeatedly. This is part of a performance optimization to prevent performance problems with multiple render calls.

When using setState(), it is recommended that the first parameter be passed as the function type parameter, because both the state and props received in the function parameter are guaranteed to be up-to-date.