react18 的 setState 还是 isBatchingUpdates 嘛?

1,783 阅读2分钟

react18 的 setState 还是 isBatchingUpdates 嘛?

导言:

react-dom版本: '18.0.0-e3b76a85c-20210802'

react版本:'18.0.0-9f88b5355-20210728'

  1. class 组件调用setState,其底层是通过ReactDOM 源码中的classComponentUpdater.enqueueSetState 方法,之后执行enqueueUpdate方法的updateQueue入队操作,而后执行scheduleUpdateOnFiber 进入批量更新操作,这里update的创建以及入队,不作讲解

    var classComponentUpdater = {
      enqueueSetState: function (inst, payload, callback) {
          var fiber = get(inst);
          var eventTime = requestEventTime();
          var lane = requestUpdateLane(fiber);
          var update = createUpdate(eventTime, lane);
          update.payload = payload;
    ​
          if (callback !== undefined && callback !== null) {
            update.callback = callback;
          }
    ​
          enqueueUpdate(fiber, update); // 将创建的update入队
          scheduleUpdateOnFiber(fiber, lane, eventTime); // 主要操作
        },
    }
    
  2. scheduleUpdateOnFiber 中会调用ensureRootIsScheduled方法,目的就是检测root的更新是否放到了scheduler(调度器)

  3. ensureRootIsScheduled阶段:

    1. 获取当前执行上下文的执行优先级

      var newCallbackPriority = getHighestPriorityLane(nextLanes);
      
    2. 获取已经在执行的优先级var existingCallbackPriority = root.callbackPriority

      var existingCallbackPriority = root.callbackPriority;
      
    3. 如果当前执行上下文的优先级 等于已经在执行优先级,便会return,这里就是为什么在react 合成事件中多次调用setState,只会被执行一次的原因了,因为后几个加到updateQueue后被return了

      if (existingCallbackPriority === newCallbackPriority && 
        !( ReactCurrentActQueue.current !== null && existingCallbackNode !== fakeActCallbackNode)) {
          return;
        }
      
    4. 如果#3的条件不满足,则会向scheduler(调度器) 中添加performConcurrentWorkOnRoot 或者performSyncWorkOnRoot

      if (newCallbackPriority === SyncLane) { // Legacy 模式
          if (root.tag === LegacyRoot) {
            if ( ReactCurrentActQueue.isBatchingLegacy !== null) {
              ReactCurrentActQueue.didScheduleLegacyUpdate = true;
            }
            scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root)); // 将 performSyncWorkOnRoot 加入SyncQueue, 合并update,待后续执行
          } else {
            scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
          }
      ​
          {
            if ( ReactCurrentActQueue.current !== null) {
              ReactCurrentActQueue.current.push(flushSyncCallbacks);
            } else {
              scheduleMicrotask(flushSyncCallbacks); // 将flushSyncCallbacks 加入微任务队列, flushSyncCallbacks 便会将 syncQueue 加入 Scheduler.scheduleCallbac
            }
          }
      ​
          newCallbackNode = null;
        } else {
          // concurrent 模式
          var schedulerPriorityLevel;
      ​
          switch (lanesToEventPriority(nextLanes)) {
            case DiscreteEventPriority:
              schedulerPriorityLevel = ImmediatePriority;
              break;
      ​
            case ContinuousEventPriority:
              schedulerPriorityLevel = UserBlockingPriority;
              break;
      ​
            case DefaultEventPriority:
              schedulerPriorityLevel = NormalPriority;
              break;
      ​
            case IdleEventPriority:
              schedulerPriorityLevel = IdlePriority;
              break;
      ​
            default:
              schedulerPriorityLevel = NormalPriority;
              break;
          }
      ​
          newCallbackNode = scheduleCallback$1(schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root));
        }
      
    5. 最后会在root上设置callbackPrioritycallbackNode

      root.callbackPriority = newCallbackPriority;
      root.callbackNode = newCallbackNode;
      

总结:setState 不是异步的,只是被加入到了scheduler的更新队列,会在下一轮任务循环中更新,导致在多次在同步任务中多次调用setState会被合并,而非合成事件则会在其他的任务队列中执行,所以不会合并在一起