react源码:为什么React不能在条件语句里面使用hooks

306 阅读2分钟

一、回顾更新过程

举个例子,这个问题让我先来回顾一下useState这个的流程,比如说我们有这样一个触发更新的函数

function toUpdate(){
    setNumber(number + 1); 
    setNumber(number + 2);
    setName("wuyz")
  }

当点击调用setNumber的时候,事实上是调用的dispatchAction函数

function dispatchAction<S, A>(
  fiber: Fiber,
  queue: UpdateQueue<S, A>,
  action: A,
) {
  
  const lane = requestUpdateLane(fiber);

  const update: Update<S, A> = {
    lane,
    action,
    eagerReducer: null,
    eagerState: null,
    next: (null: any),
  };
    //下面这块代码是创建的更新队列
  const pending = queue.pending;
  if (pending === null) {
    // This is the first update. Create a circular list.
    update.next = update;
  } else {
    update.next = pending.next;
    pending.next = update;
  }
  queue.pending = update;
  const alternate = fiber.alternate;
  if (
    fiber === currentlyRenderingFiber ||
    (alternate !== null && alternate === currentlyRenderingFiber)
  ) {
    didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate = true;
  } else {
    if (
      fiber.lanes === NoLanes &&
      (alternate === null || alternate.lanes === NoLanes)
    ) {
      const lastRenderedReducer = queue.lastRenderedReducer;
      if (lastRenderedReducer !== null) {
        let prevDispatcher;
        try {
          const currentState: S = (queue.lastRenderedState: any);
          const eagerState = lastRenderedReducer(currentState, action);
          update.eagerReducer = lastRenderedReducer;
          update.eagerState = eagerState;
          //如果两者相等那就没必要调用更新了
          if (is(eagerState, currentState)) {
            return;
          }
        } 
      }
    }
    //更新队列整理好之后调用更新
    scheduleUpdateOnFiber(fiber, lane, eventTime);
  }
}

事实上dispatchAction主要就是起到一个整理更新队列的作用,接下来就是调度的一系列任务了。

在更新函数组件里会调用 renderWithHooks,函数里面有一句

let children = Component(props, secondArg);

component是我们的组件,比如App() 然后执行这个函数组件,会再一次执行我们之前在函数顶部写过的useState等钩子函数,就是对useState这些重新执行(不过这次会根据current !== null得出是update阶段),那么这个时候就可以看到useState调用的是updateReducer而不是挂载mountReducer了,updateReducer里面有行代码是这样的.

// 获取的是之前dispatchAction函数里
  // 排列好的更新队列,updateWorkInProgressHook是获取最新的hook
  const hook = updateWorkInProgressHook();
  const queue = hook.queue;

updateWorkInProgressHook每次获取的WorkInProgress这棵树上的最新的hook,如果说在这次的renderWithHooks函数执行过程中,有一个hook没执行,那么这个hook的数据state就会被下一个state拿去用,这显然是不合理的。顺便提一下,renderWithHooks返回的是最新的fiber对象

以上的我在最近看react源码的一些见解,同时也从别人的文章里学了很多,如果有错误的欢迎大家指出~~

ps:hook的数据结构

const newHook: Hook = {
      memoizedState: null,
      baseState: null,
      baseQueue:null,
      queue: null,
      next: null,
    };