Is setState synchronous or asynchronous?

In fact, setState can appear as an asynchronous update or as a synchronous update

Asynchronous update situation

Life cycle

state = {
    number:1
};
componentDidMount(){
    this.setState({number:3})
    console.log(this.state.number)  // The output is the number before update --> 1
}
Copy the code

In a composite event

React encapsulates a set of event mechanisms and proxies for native events, such as onClick and onChange, which are common in JSX.

class App extends Component {
  state = { number: 0 }
  increment = () = > {
    this.setState({ number: this.state.number + 1 })
    console.log(this.state.number) // The output is the number before update --> 0
  }
  render() {
    return (
      <div onClick={this.increment}>
        {`Counter is: ${this.state.number}`}
      </div>)}}Copy the code

So what if we want to get the updated value? There are two main methods

Method to get the value of setState updated in real time

The callback function

SetState provides a callback function for developers to use. In the callback function, we can get the updated data in real time

state = {
    number:1
};
componentDidMount(){
    this.setState({number:3},() = >{
        console.log(this.state.number) / / 3})}Copy the code

At this point, you can see that the data printed by the console is up to date, so we can get the latest data in real time.

ComponentDidUpdate Life cycle

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      number: 0}}render() {
    return (
      <div>
        <h2>Current count: {this.state.number}</h2>
        <button onClick={e= >This.changetext ()}> Change the number</button>
      </div>)}componentDidUpdate() {
    // Method 2: Obtain the state of the asynchronous update
    console.log(this.state.number);
  }
  changeText() {
    this.setState({
      number:this.state.number + 1}}})Copy the code

Why do asynchronous updates manifest in composite events and life cycles?

It is the performance mechanism of the React framework itself. Because each call to setState triggers an update, asynchronous operations are designed to improve performance by combining multiple states together for updates and reducing render calls.

Imagine executing the following code in a component:

for ( let i = 0; i < 100; i++ ) {
    this.setState( { num: this.state.num + 1}); }Copy the code

If setState were a synchronous execution mechanism, the component would be rerendered 100 times, which is a considerable performance drain. React is obviously thinking about this as well, so it made some special optimizations for setState:

React will merge multiple setState calls into one. That is, when setState is executed, the data in the state will not be updated immediately, so the printed value will be the same as the value before the update.

Synchronizing updates

setTimeout

state = {
    number:1
};
componentDidMount(){
    setTimeout(() = >{
      this.setState({number:3})
      console.log(this.state.number) / / 3
    },0)}Copy the code

In native events

state = {
    number:1
};
componentDidMount() {
    document.body.addEventListener('click'.this.changeVal, false);
}
changeVal = () = > {
    this.setState({
      number: 3
    })
    console.log(this.state.number) / / 3
}
Copy the code

Why are these two cases synchronous updates?

When you setState in a native event, you can get the updated state value at the same time.

SetTimeout can be simply interpreted as skipping the react performance mechanism, so the updated state value can also be synchronized.

Batch updates in setState

export default class App extends Component {
  state = {
    counter: 0
  }
  render() {
    return (
      <div>
        <h2>Current count: {this.state.counter}</h2>
        <button onClick={e= > this.increment()}>+1</button>
      </div>)}increment() {
    // 1. SetState itself is merged and does not accumulate, only increments by 1
     this.setState({
       counter: this.state.counter + 1
     });
     this.setState({
       counter: this.state.counter + 1
     });
     this.setState({
       counter: this.state.counter + 1}); }}Copy the code
  • Passing objects directlysetstateIt’s going to be merged once, it’s only going to work once, it’s only going to add one.

The way I write it, it doesn’t merge, it takes effect three times.

this.setState((prevState, props) = > {
      return {
        counter: prevState.counter + 1}});this.setState((prevState, props) = > {
      return {
        counter: prevState.counter + 1}});this.setState((prevState, props) = > {
      return {
        counter: prevState.counter + 1}});Copy the code

conclusion

  • SetState is “asynchronous” in synthesized events and lifecycle functions, and synchronous in native events and setTimeout

  • Callback in the second parameter setState(partialState, callback) can be used to get the updated result. Or get the updated results in the componentDidUpdate lifecycle

  • Multiple setState updates are merged into one, and if any of the same updates are overwritten, only one is executed

Restrict state update views

How do class components limit the update effects of state?

  • PureComponent can make a shallow comparison between state and props, and if nothing changes, the component is not updated.
  • ShouldComponentUpdate The shouldComponentUpdate lifecycle can determine if the component needs to be updated by checking state changes before and after, and return true if it needs to be updated, false otherwise.