React in setState strange behavior --setState no immediate effect

Copyright Notice: Welcome to reprint, please indicate the original source https://blog.csdn.net/xiaomingelv/article/details/86348235

React setState can be said that the most frequently used in a function, and we all know, React is achieved through the management of the components of the state administration, when this.setState () is invoked, you will recall React to re-render method UI rendering

However, actual use, we will find that after sometimes we setState, did not take effect immediately, for example, we look at the sample code below

class Test extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  componentDidMount() {
    this.setState({count: this.state.count + 1});
    console.log(this.state.count); // 输出0
    this.setState({count: this.state.count + 1});
    console.log(this.state.count); // 输出0
    setTimeout(() => {
      this.setState({count: this.state.count + 1});
      console.log(this.state.count); // 输出2
      this.setState({count: this.state.count + 1});
      console.log(this.state.count); // 输出3
    }, 0);
  }

  render() {
    return <div> test </div>;
  }
}

Development process, we will find that componentDidMount method we call the value of the state after setState did not change immediately, but if we call setTimeOut inside, we have been able to get updated instantly, the reason lies in the use react based on Affairs ( portal, resolve on matters of principle asynchronous update mechanism), but for the asynchronous understanding, any connection with the asynchronous ajax different, because after all react a js framework, all operations are single-threaded, all the operation had to be in order, it specifically how to achieve it?

We all know, for the operation dom loss of performance it is very serious, so react in order to improve the overall rendering performance, will render a cycle of state merge, in this cycle, rendering you all calls to all of setState after being merged, and then a one-time rendering, so avoid calling setState frequent cause frequent operations dom, improving rendering performance. Specific implementation aspects, can simply be appreciated the existence of a state variable isBatchingUpdates to react when initially at rendering cycle, this variable is set to true, the end of the rendering cycle, is arranged to false, react will be variable according to the state when the rendering cycle, just change the current cached until the end of the rendering period, all in one portion of the flow may render ,, specifically below with reference to a flowchart

Now, we return to the beginning of the problem, why did perform setState directly in componentDidMount it can not be updated immediately, the reason is that we are in fact among the first rendering of affairs in componentDidMount, the rendering of the transaction has not been completed, and for the first time when rendering will isBatchingUpdates set to true, this is when we call setState in componentDidMount in, react will find the current transaction has not been completed, the state will be directly modified into the dirtyComponents, the waiting period to complete the final rendering, All the state to merge, one-time render. And when we put inside setTimeOut time, setTimeOut will perform the last operation into the side of the queue, that will wait for further setState rendered after the end of the period, this time the state variable has been reset back, so this time we every setState will take effect immediately

Next, we look at from the perspective of the source code is how setState operation

function enqueueUpdate(component) {
  ensureInjected();

  //不在渲染周期中
  if (!batchingStrategy.isBatchingUpdates) {
    batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }

//渲染周期中,直接缓存state等待下一步更新
  dirtyComponents.push(component);
}

This logic diagram of logic is to keep up the same, when we call setState function, in fact, eventually calls to enqueueUpdate function, has analyzed the overall logic of the above, it will not go, let's look at how the transaction is setState to render, that is batchingStrategy.batchedUpdates done in the end, to go down before, do not know if the transaction is recommended that you read this article ( portal, resolved on the principles of the transaction )

var ReactUpdates = require('ReactUpdates');
var Transaction = require('Transaction');

var emptyFunction = require('emptyFunction');

//第二个wrapper
var RESET_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: function() {
    ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  },
};

//第一个wrapper
var FLUSH_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates),
};

//wrapper列表
var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

//事务构造函数
function ReactDefaultBatchingStrategyTransaction() {
//原型中定义的初始化方法
  this.reinitializeTransaction();
}

//继承原型
Object.assign(
  ReactDefaultBatchingStrategyTransaction.prototype,
  Transaction.Mixin,
  {
    getTransactionWrappers: function() {
      return TRANSACTION_WRAPPERS;
    },
  }
);

//新建一个事务
var transaction = new ReactDefaultBatchingStrategyTransaction();

var ReactDefaultBatchingStrategy = {
  isBatchingUpdates: false,

  batchedUpdates: function(callback, a, b, c, d, e) {
    var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;

    ReactDefaultBatchingStrategy.isBatchingUpdates = true;

    // The code is written this way to avoid extra allocations
    if (alreadyBatchingUpdates) {
      callback(a, b, c, d, e);
    } else {
      //在这个地方调用事务,callback是从外部传入的方法
      transaction.perform(callback, null, a, b, c, d, e);
    }
  },
};

The above is used to render the transaction react, react is to use this transaction to render setState caused, according to the interpretation we just, we can see that at the beginning of the transaction put isBatchingUpdates settings become true, prevents a rendering cycle repeat rendering, we can also see this transaction defines two wrapper, its exports were close method for performing rendering and set the state variables, and execute rendering FLUSH_BATCHED_UPDATES first to set the execution state variables RESET_BATCHED_UPDATES, that is to say, the implementation of after rendering, this code will be executed by close of methods RESET_BATCHED_UPDATES

ReactDefaultBatchingStrategy.isBatchingUpdates = false;

After rendering the entire cycle. At this time when we execute setState re-enter a new rendering cycle

So, the question is, when we performed setState rendering cycle, how do we get to the latest value of the state of it, setTimeOut is a solution, but not very elegant, there is no other way to do that, we note that, setState provide a callback function, we only need a callback state after which you can get updates, like this

componentDidMount() {
    this.setState({count: this.state.count + 1},()=>{
    console.log(this.state.count);//该是啥就是是啥
    }));
  }

 

Guess you like

Origin blog.csdn.net/xiaomingelv/article/details/86348235