源码看React setState漫谈(一)

前端之家收集整理的这篇文章主要介绍了源码看React setState漫谈(一)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

一张图看懂React setState操作

网上关于react setState的结论不少,比如:

  • setState不会立刻改变React组件中state的值;
  • 多次setState函数调用产生的效果会合并。

但你是否真的了解setState背后的机制?真的是setState触发的刷新吗?
废话不说,先上图

组件挂载后,setState一般是通过DOM交互事件触发。这里以click为例,其他也一样。

代码很简单

  1. import React,{Component} from 'react';
  2. class MyInfo extends Component{
  3. constructor(props,context){
  4. super(props,context);
  5. this.state = {
  6. age:1
  7. }
  8. }
  9.  
  10. _grow(age){
  11. age++
  12. this.setState({
  13. age:age
  14. })
  15. }
  16.  
  17. render(){
  18. const {age} = this.state
  19. return (
  20. <div>
  21. 我的年龄是{age}
  22. <button onClick={this._grow.bind(this,age)}>点击涨一岁</button>
  23. </div>
  24. )
  25. }
  26. }
  27.  
  28. export default MyInfo;

我们点击button按钮时,到底发生了什么?ReactEventListener会触发dispatchEvent方法。(具体怎么触发是事件机制的事,这里不深究)

  1. dispatchEvent: function (topLevelType,nativeEvent) {
  2. if (!ReactEventListener._enabled) {
  3. return;
  4. }
  5.  
  6. var bookKeeping = TopLevelCallbackBookKeeping.getPooled(topLevelType,nativeEvent);
  7. try {
  8. // Event queue being processed in the same cycle allows
  9. // `preventDefault`.
  10. ReactUpdates.batchedUpdates(handleTopLevelImpl,bookKeeping);
  11. } finally {
  12. TopLevelCallbackBookKeeping.release(bookKeeping);
  13. }
  14. }

可以看到这里有个ReactUpdates.batchedUpdates方法。我们跟进去看看

  1. function batchedUpdates(callback,a,b,c,d,e) {
  2. ensureInjected();
  3. return batchingStrategy.batchedUpdates(callback,e);
  4. }

可以发现这里调用batchingStrategy方法。这又是什么鬼,其实这是注入进来的ReactDefaultBatchingStragy这里插一句,React大量运用了注入机制,这样每次注入的都是同一个实例化对象,防止多次实例化。
到这边就已经开启了批量更新模式
继续看,

  1. batchedUpdates: function(callback,e) {
  2. var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
  3.  
  4. ReactDefaultBatchingStrategy.isBatchingUpdates = true;
  5.  
  6. // The code is written this way to avoid extra allocations
  7. if (alreadyBatchingUpdates) {
  8. return callback(a,e);
  9. } else {
  10. return transaction.perform(callback,null,e);
  11. }
  12. },

transaction.perform执行了一个事务。事务其他文章说的很多我就不详细解释了。大概就是,transaction在执行perform之前会执行特性的initialize方法,然后执行传进去的callback,之后会执行close方法,是不是似曾相识?没错,高阶函数或者高阶组件都是这路数。
ReactDefaultBatchingStragy里可以发现

  1. var transaction = new ReactDefaultBatchingStrategyTransaction();
  2.  
  3. //在事务结束时清理一下标识
  4. var RESET_BATCHED_UPDATES = {
  5. initialize: emptyFunction,close: function() {
  6. ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  7. },};
  8.  
  9. // 在事务结束时执行flushBatchedUpdates方法,这个方法就是 state 更新的核心代码了。
  10. var FLUSH_BATCHED_UPDATES = {
  11. initialize: emptyFunction,close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates),};
  12.  
  13. var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES,RESET_BATCHED_UPDATES];
  14.  
  15. function ReactDefaultBatchingStrategyTransaction() {
  16. this.reinitializeTransaction();
  17. }
  18.  
  19. Object.assign(ReactDefaultBatchingStrategyTransaction.prototype,Transaction,{
  20. getTransactionWrappers: function() {
  21. return TRANSACTION_WRAPPERS;
  22. },});

可以发现这个transition有两个wrapper,主要看FLUSH_BATCHED_UPDATES

目前走完了图中的第一行,有点晕的可以对着图回顾一下。

-

是不是发现哪里不对?是的,到现在我们的setState还没执行呢!
接着上文,我们首先看看transition的两个initailize方法,发现时两个空函数。跳过
接着就是perform需要执行的逻辑了。再次放出代码

  1. ReactUpdates.batchedUpdates(handleTopLevelImpl,bookKeeping);

也就是执行这边的handleTopLevelImpl。正是在这边调用DOM事件对应的回调方法。也就是例子中_grow在这时候调用
然后是setState方法。这里和大部分书和文章说的差不多。抛开细节就是将state的变化和对应的回调函数放置到_pendingStateQueue,和_pendingCallback中。
然后把需要更新的组件放到dirtyComponents序列中。
重点来了:
注意注意!!!!
setState从来不负责更新操作。它的工作只是把state,和callback放进序列,并且把要更新的组件放到dirtyComponents序列
还记得吗?我们还在ReactDefalutBatchingStragy的事务中,perform执行完了,还要执行close。
真正执行更新方法的是close里面的flushBatchedUpdates。鉴于文章长度,其他的可以看图理解

猜你在找的React相关文章