React users must be exposed to the setState API

But to use good must grasp three key words: merge update, synchronous asynchronous, immutable value

At first glance, some friends may be in a fog

Don’t worry. I’ll explain them one by one

1. SetState will merge updates

  • Default merge update

Let’s start with an example 🌰

import { Component } from 'react' class App extends Component { state= { count: 1 } handleAdd = () => { 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> { this.state.count } <button onClick={ this.handleAdd }>add</button>  </div> ) } }Copy the code

So in this code, we’re repeating setState to keep increasing count by one

But we’ll see that clicking the button once only adds 1

Instead of let’s think about adding 4

React is an underlying optimization

He will merge the updated objects in multiple SETStates into one update

Similarly, update objects are merged using object. assign

// Equivalent to {count: this.state.count + 1} object. assign({count: this.state.count + 1}, {count: this.state.count + 1 }, { count: this.state.count + 1 }, { count: this.state.count + 1 })Copy the code

So how do you avoid not being merged?

  • Functions are not merged

You can use functions, functions are not merged

// preProps props this. SetState ((preState, preProps) => {// return the object to be updated this time})Copy the code

Let’s take an example 🌰

import { Component } from  'react'

class App extends Component {
  state= {
      count: 1
  }
  handleAdd = () => {
      this.setState((preState) => {
          console.log(preState) // {count: 1}
          return ({
              count: preState.count + 1
          })
      })
      this.setState((preState) => {
          console.log(preState) // {count: 2}
          return ({
              count: preState.count + 1
          })
      })
      this.setState((preState) => {
          console.log(preState) // {count: 3}
          return ({
              count: preState.count + 1
          })
      })
      this.setState((preState) => {
          console.log(preState) // {count: 4}
          return ({
              count: preState.count + 1
          })
      })
  }
  render() {
    return (
        <div>
          { this.state.count }
          <button onClick={ this.handleAdd }>add</button>
        </div>
    )
  }
}
Copy the code

2. Asynchronous synchronization is possible

  • The default is asynchronous

What does that mean?

Let’s take a direct example 🌰

import { Component } from 'react' class App extends Component { state= { count: 1 } handleAdd = () => { this.setState({ count: this.state.count + 1 }) console.log(this.state.count) } render() { return ( <div> { this.state.count } <button onClick={  this.handleAdd }>add</button> </div> ) } }Copy the code

Effect:

Looking at the renderings, small partners can actually be found at a glance

You actually setState before console.log

However, the obtained value is still printed from 1, not 2

So console.log hasn’t setState yet

Then setState is an asynchronous update

But we can use setState(updater, callback) as the second argument (callback function)

Similar to $nextTick in Vue

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

Of course you can also get the values for all completed updates at Life Cycle componentDidUpdate

ComponentDidUpdate () {console.log(this.state.count)}Copy the code

So when do you synchronize?

  • Synchronization cases include using asynchronous functions and custom events

Asynchronous functions can be setTimeout, setInterval, requestAnimationFrame

class App extends Component { state= { count: 1 } handleAdd = () => { setTimeout(() => { this.setState({ count: This.state.count + 1}) console.log(this.state.count) 0) } render() { return ( <div> { this.state.count } <button onClick={this.handleAdd}>add</button> </div> ) } }Copy the code

Effect:

It’s easy to see from the code and the diagram above

We’ll setState again before console.log

But the print starts at 2

So that means that setState is being updated synchronously

Similar to custom events, attached is an example 🌰

import { Component } from 'react' class App extends Component { state= { count: 1} componentDidMount() {// custom event this.handleadd = event => {this.setState({count: This.state.count + 1}) console.log(this.state.count) // Count = 2} const button = document.getelementById ('button')  button.addEventListener('click', this.handleAdd); } componentWillUnmount() {// Remove custom events, To prevent a memory leak const button = document. GetElementById (" button ") button. The removeEventListener (' click ', enclosing handleAdd); } render() { return ( <div> { this.state.count } <button id='button'>add</button> </div> ) } }Copy the code
  • The principle of synchronization or asynchronism

This is a classic picture

(Figure from network)

What does this picture mean?

React use isBatchingUpdates to determine setState

Let’s take click events as an example 🌰

React will precede setState with isBatchingUpdates = true

At the end, isBatchingUpdates = false

At this point, hit the left judgment of the diagram: save the component in dirtyComponents

HandleAdd = () => {// Start with BathingUpdates, isBatchingUpdates = true this.setState({count: This.state.count + 1}) console.log(this.state.count)Copy the code

What about synchronization?

Likewise, before setState, isBatchingUpdates = true

End with isBatchingUpdates = false

At this point, isBatchingUpdates = false before setState starts working

Hit the judgment on the right of the graph, and immediately start a series of updates

HandleAdd = () => {// ① Start with BathingUpdates, isBatchingUpdates = true setTimeout(() => {this.setState({count: This.state.count + 1}) console.log(this.state.count)Copy the code

3. SetState is best used with immutable values

Immutable values are used for page performance optimization

We know that some page components don’t need to be updated

For example, when the parent component updates, the child component updates with it

But the child component doesn’t actually have numeric updates, so there’s no need to update the render

We can manually control the update rendering using shouldComponentUpdate

shouldComponentUpdate(nextProps, nextState) { //... // If return true means that the component does not update the render}Copy the code

So, we’re going to use immutable values, two props or state objects that are different from each other

For example,

/ /... // Pass the array list shouldComponentUpdate(nextProps, NextState) {/ / introduce lodash isEqual more array values are equal if (isEqual (nextProps. List, this. Props. List)) / / component does not update} {return false Return false // Component does not update}Copy the code

If you don’t use immutable values, it could be pointing to the same object

So even if you use shouldComponentUpdate, it will update the render

Because they’re all the same thing

If you don’t understand

So let’s see what I said in the article I wrote before about react setState being immutable, right? Very detailed ~

Thanks for reading ~