react hooks setState执行原理

130 阅读1分钟

React 中useState的执行过程

React在reconcile过程中会根据vnode生成Fiber树,当遇到函数组件时,会执行函数组件获取组件的children,当遇到useState时,会执行useState初始化状态,

使用useState时我们会从react中引入useState,如下:

const {useState} from 'react;

react中的源码如下:

function useState(initialState) {
  var dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}

function resolveDispatcher() {
  var dispatcher = ReactCurrentDispatcher.current;
  return dispatcher;
}

执行useState相当于执行ReactCurrentDispatcher.current的useState方法,ReactCurrentDispatcher.current的具体赋值是在react-dom的renderWithHooks函数中,如下:

function renderWithHooks() {
    ...省略
    ReactCurrentDispatcher.current = HooksDispatcherOnMountInDEV;
    // 执行函数组件,获取组件子节点
    let children = Component(props, secondArg);
    // didScheduleRenderPhaseUpdateDuringThisPass 全局flg
    if (didScheduleRenderPhaseUpdateDuringThisPass) {
        do {
          didScheduleRenderPhaseUpdateDuringThisPass = false;
          currentHook = null;
          workInProgressHook = null;

          workInProgress.updateQueue = null;

          ReactCurrentDispatcher.current = __DEV__
            ? HooksDispatcherOnRerenderInDEV
            : HooksDispatcherOnRerender;
          children = Component(props, secondArg);
      } while (didScheduleRenderPhaseUpdateDuringThisPass);
  }
}

const HooksDispatcherOnMountInDEV ={
    useState: function (initialState) {
    //标记当前执行的hook名称
      currentHookNameInDev = 'useState';
      //处理hookTypesDev,做一些校验
      mountHookTypesDev();
      
      var prevDispatcher = ReactCurrentDispatcher$1.current;
      ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;

      try {
        return mountState(initialState);
      } finally {
        ReactCurrentDispatcher$1.current = prevDispatcher;
      }
    },

}

function mountState(initialState) {
   var hook = mountWorkInProgressHook();
    //初始化memoizedState 链表对象
    //hook= { baseQueue: null
    //  baseState: null
    //  memoizedState: null
    //  next: null
    //  queue: null
    //}
    
  //如果useState初始化传入的是函数则执行这个函数
  if (typeof initialState === 'function') {
    initialState = initialState();
  }

  hook.memoizedState = hook.baseState = initialState;
  
  var queue = hook.queue = {
    pending: null,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: initialState
  };
  //将queue保存在Fiberd对象的memoizedState属性上,即保存了state的初始值又保存了setState的函数引用
  var dispatch = queue.dispatch = dispatchAction.bind(null, currentlyRenderingFiber$1, queue);
  return [hook.memoizedState, dispatch];
}

如上可以拿到初始化的state和setState的函数,当调用setSatate时会指向dispatchAction(fiber, queue, action)函数,其中fiber是当前useState所在的函数组件的fiber对象,queue为fiebr上的上次state(既fiber.memoizedState),action为新的状态,具体调用如下:

setState.jpg

React在reconcile过程中会再次执行函数组件获取组件的children,当再次执行useState时,

function renderWithHooks(){
 ...省略
 // 此时的ReactCurrentDispatcher.current 等于HooksDispatcherOnUpdateInDEV,而不是HooksDispatcherOnMountInDEV
    ReactCurrentDispatcher.current = HooksDispatcherOnUpdateInDEV;
}

const   HooksDispatcherOnUpdateInDEV = { 
    useState: function (initialState) {
      currentHookNameInDev = 'useState';
      updateHookTypesDev();
      var prevDispatcher = ReactCurrentDispatcher$1.current;
      ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;

      try {
        return updateState(initialState);
      } finally {
        ReactCurrentDispatcher$1.current = prevDispatcher;
      }
    }
}

function updateState(initialState) {
  return updateReducer(basicStateReducer);
}


function updateReducer(reducer, initialArg, init) {
//
  var hook = updateWorkInProgressHook();
  var queue = hook.queue;

  queue.lastRenderedReducer = reducer;
  var current = currentHook; // The last rebase update that is NOT part of the base state.

  var baseQueue = current.baseQueue; // The last pending update that hasn't been processed yet.

  var pendingQueue = queue.pending;

  if (pendingQueue !== null) {
    // We have new updates that haven't been processed yet.
    // We'll add them to the base queue.
    if (baseQueue !== null) {
      // Merge the pending queue and the base queue.
      var baseFirst = baseQueue.next;
      var pendingFirst = pendingQueue.next;
      baseQueue.next = pendingFirst;
      pendingQueue.next = baseFirst;
    }
    current.baseQueue = baseQueue = pendingQueue;
    queue.pending = null;
  }

  if (baseQueue !== null) {
    // We have a queue to process.
    var first = baseQueue.next;
    var newState = current.baseState;
    var newBaseState = null;
    var newBaseQueueFirst = null;
    var newBaseQueueLast = null;
    var update = first;

    do {
      var updateLane = update.lane;

      if (!isSubsetOfLanes(renderLanes, updateLane)) {
        // Priority is insufficient. Skip this update. If this is the first
        // skipped update, the previous update/state is the new base
        // update/state.
        var clone = {
          lane: updateLane,
          action: update.action,
          eagerReducer: update.eagerReducer,
          eagerState: update.eagerState,
          next: null
        };

        if (newBaseQueueLast === null) {
          newBaseQueueFirst = newBaseQueueLast = clone;
          newBaseState = newState;
        } else {
          newBaseQueueLast = newBaseQueueLast.next = clone;
        }

        currentlyRenderingFiber$1.lanes = mergeLanes(currentlyRenderingFiber$1.lanes, updateLane);
        markSkippedUpdateLanes(updateLane);
      } else {
        // This update does have sufficient priority.
        if (newBaseQueueLast !== null) {
          var _clone = {
            lane: NoLane,
            action: update.action,
            eagerReducer: update.eagerReducer,
            eagerState: update.eagerState,
            next: null
          };
          newBaseQueueLast = newBaseQueueLast.next = _clone;
        } // Process this update.


        if (update.eagerReducer === reducer) {
          // If this update was processed eagerly, and its reducer matches the
          // current reducer, we can use the eagerly computed state.
          newState = update.eagerState;
        } else {
          var action = update.action;
          newState = reducer(newState, action);
        }
      }

      update = update.next;
    } while (update !== null && update !== first);

    if (newBaseQueueLast === null) {
      newBaseState = newState;
    } else {
      newBaseQueueLast.next = newBaseQueueFirst;
    } // Mark that the fiber performed work, but only if the new state is
    // different from the current state.


    if (!objectIs(newState, hook.memoizedState)) {
      markWorkInProgressReceivedUpdate();
    }

    hook.memoizedState = newState;
    hook.baseState = newBaseState;
    hook.baseQueue = newBaseQueueLast;
    queue.lastRenderedState = newState;
  }

  var dispatch = queue.dispatch;
  return [hook.memoizedState, dispatch];
}