React源码-setState

19 篇文章 0 订阅

setState

调用过程

setState->this.updater.enqueueSetState->scheduleWork->requestWork
  • enqueueSetState
const classComponentUpdater = {
  ...
  enqueueSetState(inst, payload, callback) {
    // 获取当前组件的instance
    var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');

     // 将要更新的state放入一个数组里
     var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
    queue.push(partialState);
    
    // 关键代码
    enqueueUpdate(fiber, update); // 原生事件,因为
    scheduleWork(fiber, expirationTime);
  },
  ...


  • enqueueUpdate
function enqueueUpdate(component) {
 
  if (!batchingStrategy.isBatchingUpdates) {
    batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }
  dirtyComponents.push(component);
}



  • batchedUpdates
var ReactDefaultBatchingStrategy = {
  // 用于标记当前是否出于批量更新
  isBatchingUpdates: false,
  // 当调用这个方法时,正式开始批量更新
  batchedUpdates: function (callback, a, b, c, d, e) {
    var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;

    ReactDefaultBatchingStrategy.isBatchingUpdates = true;

    // 如果当前事务正在更新过程在中,则调用callback,既enqueueUpdate
    if (alreadyBatchingUpdates) {
      return callback(a, b, c, d, e);
    } else {
    // 否则执行更新事务
      return transaction.perform(callback, null, a, b, c, d, e);
    }
  }
};


  • scheduleWork
if (
    !isWorking ||
    isCommitting ||
    nextRoot !== root
  ) {
    const rootExpirationTime = root.expirationTime;
    // 关键执行requestWork
    requestWork(root, rootExpirationTime);
  }



  • requestWork
function requestWork(root, expirationTime) {
  // 将根节点添加到调度任务中
  addRootToSchedule(root, expirationTime)
  
  // 第一步,isRendering在生命周期中为true,在合成事件和原生事件(不存在)
  if (isRendering) {
    return;
  }
  
  // isBatchingUpdates、isUnbatchingUpdates是全局变量
  // isBatchingUpdates一开始默认为false,合成事件中会被设置为true

  if (isBatchingUpdates) {
    if (isUnbatchingUpdates) {
      ....
      performWorkOnRoot(root, Sync, false);
    }
    return;
  }
  
  if (expirationTime === Sync) {
    performSyncWork();
  } else {
    scheduleCallbackWithExpirationTime(root, expirationTime);
  }
}


三种调方式

  • 生命周期调用
  • 合成事件调用
  • 其他情况调用(原声事件,异步调用)

生命周期调用

  • 案列一:componentdidmount
state = {
    age: 0,
    name: null
}

componentDidMount() {
    this.setState({age:1})
    console.log(this.stage.age); // 0
    this.setState({name:'test'})
    this.setState({age:1})
    console.log(this.stage.name); // null
}

分析:

1 、在每个组件渲染之前调用batchedUpdate(),此时isBatchingUpdates由false --> true了

将 React 组件渲染到 DOM 中的过程就处于一个大的 Transaction 中
所以会直接调用batchedUpdate()

// ReactMount.js
ReactUpdates.batchedUpdates(
      batchedMountComponentIntoNode,  // 负责初始渲染组件
      componentInstance,
      container,
      shouldReuseMarkup,
      context,
);




为什么首次渲染组件要调用batchedUpdates?

因为组件渲染会调用生命周期,生命周期可能会调用setState
开启批量更新,存储更新,在事务结束后,去批量跟新

2 、组件初始化的时候会调用performWorkOnRoot,将isRendering = true,然后:
constructor -> getDerivedStateFromProps -> render -> componentDidMount

3、此时setState走向流程如下:

function enqueueUpdate(component) {
 
  if (!batchingStrategy.isBatchingUpdates) {
    batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }
  dirtyComponents.push(component); // 走这一步,因为isBatchingUpdates:true
}


在requestWork中走第一步直接被return

所以案列一的执行过程:

  • 第一步: this.setState({age:1})
_pendingStateQueue = [{age: 1}]

因为isBatchingUpdates为true
enqueUpdate()不走更新,走dirtyComponents.push(component)

因为isRendering为true,
requestWork // 走第一步,return,不走performSyncWork()去更新组件

  • 第二步:console.log(this.stage.age);

因为第一步并没有实际去更新组件,所以此时的state为发生改变


  • 第三步:this.setState()


  • 第四步:this.setState({age:1})
_pendingStateQueue = [{age: 1},{name:'test'}, {age:1}]

接下来步骤,同第一步一样

然后生命周期执行完,这个时候产生了待更新的_pendingStateQueue,和待更新的dirty组件


  • 第五步:什么时候去更新组件了?
开始说了,react组件渲染DOM的过程就是一个大的事务(Transaction)中
ReactUpdates.batchedUpdates(
      batchedMountComponentIntoNode,  // 负责初始渲染组件
      componentInstance,
      container,
      shouldReuseMarkup,
      context,
);

batchedUpdates(){
    var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
    ReactDefaultBatchingStrategy.isBatchingUpdates = true;

    if (alreadyBatchingUpdates) {
      return callback(a, b, c, d, e);
    } else {
        // 走这一步
    return transaction.perform(batchedMountComponentIntoNode, null, a, b, c, d, e);
    }
}

等batchedMountComponentIntoNode(组件初始化渲染完以后,生命周期走完)执行完以后

执行事务close方法中的flushBatchedUpdates,真正去更新组件

### 说说事务transaction

  • transaction
  wrappers (injected at creation time)
 *                                      +        +
 *                                      |        |
 *                    +-----------------|--------|--------------+
 *                    |                 v        |              |
 *                    |      +---------------+   |              |
 *                    |   +--|    wrapper1   |---|----+         |
 *                    |   |  +---------------+   v    |         |
 *                    |   |          +-------------+  |         |
 *                    |   |     +----|   wrapper2  |--------+   |
 *                    |   |     |    +-------------+  |     |   |
 *                    |   |     |                     |     |   |
 *                    |   v     v                     v     v   | wrapper
 *                    | +---+ +---+   +---------+   +---+ +---+ | invariants
 * perform(anyMethod) | |   | |   |   |         |   |   | |   | | maintained
 * +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | +---+ +---+   +---------+   +---+ +---+ |
 *                    |  initialize                    close    |
 *                    +-----------------------------------------+


事务执行顺序: initialize --> anyMethod --> close (wrapper过程)
这里案例中anyMethod指batchedMountComponentIntoNode

react默认的批量更新策略,transaction包含了两个wrapper

1 FLUSH_BATCHED_UPDATES ,负责实际更新组件
2 RESET_BATCHED_UPDATES , 重置isBatchingUpdates = false

FLUSH_BATCHED_UPDATES

  • FLUSH_BATCHED_UPDATES,

var FLUSH_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
};

  • flushBatchedUpdates

var flushBatchedUpdates = function () {
      // close前执行完runBatchedUpdates方法,这是关键
      transaction.perform(runBatchedUpdates, null, transaction);
   ...
  }
};

  • runBatchedUpdates
又经过一系列函数调用,合并state然后更新组件



RESET_BATCHED_UPDATES

var RESET_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: function () {
    ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  }
};


合成事件调用

  • 案例二
state = {
    age: 0,
    name: null
}

onClic = () => {
    this.setState({age:1})
    console.log(this.stage.age); // 0
    this.setState({name:'test'})
    this.setState({age:1})
    console.log(this.stage.name); // null
}



合成事件关键点:
1、 合成事件中,isBatchingUpdates 被设置为 true;
2、分为try,finally两部分,finally中去实际更新

  • 案列二实际执行流程
var previousIsBatchingUpdates = isBatchingUpdates;
    isBatchingUpdates = true; 

try {
    this.setState({age:1}) 
    console.log(this.stage.age); // 0
    this.setState({name:'test'})
    this.setState({age:1})
    console.log(this.stage.name); // null
} finally {
    isBatchingUpdates = previousIsBatchingUpdates; 
    if (!isBatchingUpdates && !isRendering) { // isRendering为false在合成事件中
        performSyncWork(); //去更新组件
    }
}



也为isBatchingUpdates为true,isRendering为false所以try代码块中的所有setState的
enqueUpdate() requestWork()都不会更新组件,只会将其放入dirty组件中

原生事件

1 没有合成事件将isBatchingUpdates是默认值false,isRendering不存在

  • enqueueUpdate中
function enqueueUpdate(component) {
 
 // 走这里直接更新
  if (!batchingStrategy.isBatchingUpdates) {
    batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }
  dirtyComponents.push(component);
}



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

    ReactDefaultBatchingStrategy.isBatchingUpdates = true;

    if (alreadyBatchingUpdates) {
      return callback(a, b, c, d, e);
    } else {
    // 走这里直接更新,callback就是enqueUpdate
      return transaction.perform(callback, null, a, b, c, d, e);
    }
  }
};

  • requestWork这里怎么走?
isBatchingUpdates在batchedUpdates中已经被设置为true了

requestWork这里应该走第二步直接被return


setTimeout

无论他在合成事件还是,原生事件,还是生命周期,由于他是宏任务,等到他执行的时候
并没有isRendering和isBatchingUpdates,直接走requestWork的第三个

参考文章

https://zhuanlan.zhihu.com/p/35801438
https://yeungkc.com/2019/06/30/web-react-set-state/
http://zxc0328.github.io/2017/09/28/react-16-source/ Fiber版本
https://yeungkc.com/2019/06/30/web-react-set-state/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值