Analysis of the principle of this.setState method in React (detailed explanation)

Summary

We know that there is no two-way data binding effect like Vue in React. The this.setState method is used to change the data. The data changed by this.setState method will cause the render of the component to re-render and refresh the data.

And this article is mainly to simply implement the this.setState method. In order to implement this method, it is necessary to know what characteristics the this.setState method has.

First in the React component, we first define a state and setState method:

  myState = {
    
    
    value: 0
  }
  
  mySetState = ( changeState ) =>{
    
    
  
    this.setState(
      this.myState
    )
  }

It may be said here, why do you need to call React's setState in the mySetState method you wrote? Is it still written by myself after calling other people's?

Since in React, render can only handle values ​​modified by the setState method, so here we call it in mySetState. But the specific implementation of the mySetState method is still done by ourselves.

1. Asynchronous setState

First, let's look at a piece of code:

  state = {
    
    
    value: 0
  }
    setTimeout(() => {
    
    
      console.log('SetTimeOut ---- '+this.state.value);
    }, 0);
    new Promise((resolve,reject) => {
    
    
      resolve(this.state.value)
    })
    .then(res => {
    
    
      console.log('Promise ---- '+res);
    })

    this.setState({
    
    
      value: this.state.value+1
    })

    console.log(this.state.value);

What will this code output?
insert image description here
From this result, we can know the execution order of the above code:


(1) console.log(this.state.value);

(2) Promise code

(3) setState method

(4) setTimeOut method

So setState must be an asynchronous method.

When implementing, pay attention to the asynchronous problem.

2. Multiple setState methods

Let's continue to look at a piece of code:

    this.setState({
    
    
      value: this.state.value+1
    })
    this.setState({
    
    
      value: this.state.value+1
    })
    this.setState({
    
    
      value: this.state.value+1
    })
    this.setState({
    
    
      value: this.state.value+1
    })
    this.setState({
    
    
      value: this.state.value+1
    })

    setTimeout(() => {
    
    
      console.log(this.state.value);
    }, 0);

Will this code output 1 or 5?

The answer is 1, which is an optimization made by React for efficiency. Prevent every setState from causing render to re-render!

And if I want this effect, React also provides a solution, that is, the setState method can accept a function as a parameter:

    this.setState( (preState) => {
    
    
      return {
    
    
        value: preState.value+1
      }
    } )

    this.setState( (preState) => {
    
    
      return {
    
    
        value: preState.value+1
      }
    } )

    this.setState( (preState) => {
    
    
      return {
    
    
        value: preState.value+1
      }
    } )

    this.setState( (preState) => {
    
    
      return {
    
    
        value: preState.value+1
      }
    } )

    setTimeout(() => {
    
    
      console.log(this.state.value);
    }, 0);

preState represents the previous state.

3. Manually implement mySetState

OK, with the above understanding of the setState method, we can manually implement the mySetState method

The first solution is when multiple setState methods are called, so we cannot update the state value every time the mySetState method is called, that is, write like this:

  mySetState = ( changeState ) =>{
    
    

    Object.assign(this.myState,changeState)

    this.setState(
      this.myState
    )
  }

We only need to know the last changeState, so here we maintain a queue to save all changeStates, and then put the changeState into the queue every time the mySetState method is called :


  queue = []
  
  mySetState = ( changeState ) =>{
    
    

    Promise.resolve().then(()=>{
    
    
      this.stateShift()
    })

    this.queue.push(changeState)

    this.setState(
      this.myState
    )
  }

Here comes the main event, we already have this queue, so how do we make the changeState of this queue come out?

Here we write a method:

  stateShift() {
    
    
    let empty;
    while(empty = this.queue.shift()){
    
    
      
    }
  }

Traverse the queue by means of an iterator. Empty is each changeState obtained. There are two forms of changeState, and it is definitely easy to write when it is an object.

  stateShift() {
    
    
    let empty;
    while(empty = this.queue.shift()){
    
    
      if(typeof empty === 'object'){
    
    
        Object.assign(this.myState,empty)
      }
  }

Just let the new ones replace the old ones. If changeState is a method, at this time, we need to manually maintain a preState variable, which is used to save the previous state .

So the specific implementation should be:

  stateShift() {
    
    
    let empty;
    while(empty = this.queue.shift()){
    
    
      if(!this.preState){
    
    
        this.preState =  Object.assign({
    
    },this.myState)
      }
      if(typeof empty === 'object'){
    
    
        Object.assign(this.myState,empty)
      }else if(typeof empty === 'function'){
    
    
        Object.assign(this.myState,empty(this.preState))
      }

      this.preState = this.myState
    }
  }

The last step, when should this method be called. In fact, what needs to be paid attention to is nothing more than putting all changeStates in the queue before calling them.

And this.queue.push(changeState) is a synchronous code, so this effect can be achieved by calling it asynchronously before it :

  mySetState = ( changeState ) =>{
    
    

    Promise.resolve().then(()=>{
    
    
      this.stateShift()
    })

    this.queue.push(changeState)

    this.setState(
      this.myState
    )
  }

Finally, attach the entire implementation code:

import React, {
    
     Component } from 'react'

export default class Child extends Component {
    
    

  state = {
    
    
    value: 0
  }

  myState = {
    
    
    value: 0
  }

  queue = []

  mySetState = ( changeState ) =>{
    
    

    Promise.resolve().then(()=>{
    
    
      this.stateShift()
    })

    this.queue.push(changeState)

    this.setState(
      this.myState
    )
  }


  stateShift() {
    
    
    let empty;
    while(empty = this.queue.shift()){
    
    
      if(!this.preState){
    
    
        this.preState =  Object.assign({
    
    },this.myState)
      }
      if(typeof empty === 'object'){
    
    
        Object.assign(this.myState,empty)
      }else if(typeof empty === 'function'){
    
    
        Object.assign(this.myState,empty(this.preState))
      }

      this.preState = this.myState
    }
  }

  add = ()=>{
    
    
    //测试代码
    // this.mySetState( (pre) => {
    
    
    //   return {
    
    
    //     value: pre.value + 1
    //   }
    // } )
    // this.mySetState( (pre) => {
    
    
    //   return {
    
    
    //     value: pre.value + 1
    //   }
    // } )
    // this.mySetState( (pre) => {
    
    
    //   return {
    
    
    //     value: pre.value + 1
    //   }
    // } )

    // this.mySetState({
    
    
    //   value: this.myState.value+1
    // })
    // this.mySetState({
    
    
    //   value: this.myState.value+1
    // })
    // this.mySetState({
    
    
    //   value: this.myState.value+1
    // })
    // this.mySetState({
    
    
    //   value: this.myState.value+1
    // })

    setTimeout(() => {
    
    
      console.log('SetTimeOut ---- '+this.myState.value);
    }, 0);
    new Promise((resolve,reject) => {
    
    
      resolve(this.myState.value)
    })
    .then(res => {
    
    
      console.log('Promise ---- '+res);
    })

    this.mySetState({
    
    
      value: this.myState.value+1
    })

    console.log(this.myState.value);


  }

  render() {
    
    

    return (
      <div>
        <span>{
    
    this.myState.value}</span>
        <br></br>
        <button onClick={
    
    this.add}>++</button>
      </div>
    )
  }
}

Finally, it is worth noting that here is just a simulation of the implementation of setState for some characteristics of setState. The specific source code may not be as simple as this!

Guess you like

Origin blog.csdn.net/weixin_46726346/article/details/126610535