在每一个hook执行时,会赋值会全局变量firstWorkInProgressHook 在组件完成时会被挂载在memoizedState effect放在updateQueue属性中,构建完成以后是一条单向链表

dispatch函数调用时,会把更新之后的值挂载在queue的last属性上也是链表

构建的过程也就是将要执行的函数组件对应的fiber节点赋值为全局变量,对应的hook挂载在全局变量中,执行完成以后把hook链表再放在当前fiber节点上。

React hook 链表实现

  1. // 在每个hook挂载时的执行函数。 保存在全局变量中。 形成链表
  2. function mountWorkInProgressHook() {
  3. var hook = {
  4. memoizedState: null,
  5. baseState: null,
  6. queue: null,
  7. baseUpdate: null,
  8. next: null
  9. };
  10. if (workInProgressHook === null) {
  11. // 挂载到最后一个
  12. firstWorkInProgressHook = workInProgressHook = hook;
  13. } else {
  14. // 增加一个
  15. workInProgressHook = workInProgressHook.next = hook;
  16. }
  17. return workInProgressHook;
  18. }
  19. // 最终的hook链表, 保存在函数组件对应fiberNode上
  20. fiberNode.memoizedState = firstWorkInProgressHook = {
  21. // 组件内最终使用的值
  22. memoizedState: null,
  23. baseState: null,
  24. queue:null | {
  25. // 链接action形成的update对象
  26. last: null,
  27. dispatch: null,
  28. eagerReducer: basicStateReducer,
  29. eagerState: initialState
  30. },
  31. baseUpdate: null,
  32. // 指向下一个hook节点
  33. next: null
  34. }
  35. // update对象,会挂载在首次mount时绑定的queue对象的last指针上
  36. const update = {
  37. expirationTime: _expirationTime,
  38. action: action,
  39. eagerReducer: null,
  40. eagerState: null,
  41. // 链接下一个update对象的指针,会形成一个环状链表
  42. next: null
  43. }

useState的更新也是异步的,多个更新会形成多节点的环状链表。更新之后此次的action对象会赋值给 baseUpdate属性,在第二次更新时baseUpdate不等于null,环状链表会被剪开,最后返回更新后的值

为hook添加可更新的功能

dispatchAction 函数在hook中使用, 构建update对象之后会调度这次更新

mountStae:

  1. function mountState<S>(
  2. initialState: (() => S) | S,
  3. ): [S, Dispatch<BasicStateAction<S>>] {
  4. // 获取下一个需要挂载的hook对象,
  5. const hook = mountWorkInProgressHook();
  6. if (typeof initialState === 'function') {
  7. initialState = initialState();
  8. }
  9. // 挂载hook绑定的state
  10. hook.memoizedState = hook.baseState = initialState;
  11. // 创建一个hook的更新队列
  12. const queue = (hook.queue = {
  13. pending: null,
  14. dispatch: null,
  15. lastRenderedReducer: basicStateReducer,
  16. lastRenderedState: (initialState: any),
  17. });
  18. const dispatch = (queue.dispatch = (dispatchAction.bind(
  19. null,
  20. currentlyRenderingFiber, // 当前执行的function组件对应的fiber节点
  21. queue, // 一个hook对应的更新队列
  22. ): any));
  23. return [hook.memoizedState, dispatch];
  24. }

dispatchAction:

// 删除了dev环境的运行时
function dispatchAction<S, A>(
  fiber: Fiber,
  queue: UpdateQueue<S, A>,
  action: A,
) {
  // 获取当前时间
  const eventTime = requestEventTime();
  // 获取当前可用的lane.
  const lane = requestUpdateLane(fiber);

  // 创建一个updae对象
  const update = {
    lane,
    action,
    eagerReducer: null,
    eagerState: null,
    next: (null: any),
  };

  // 循环链表
  const pending = queue.pending;
  if (pending === null) {
    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)
  ) {
    // 首次挂载时的更新并不会立即执行,会在commit阶段的结尾重新调度一次更新
    didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate = true;
  } else {
    // 当前时第一次更新,必须第一时间计算最新的state
    if (
      fiber.lanes === NoLanes &&
      (alternate === null || alternate.lanes === NoLanes)
    ) {
      // lastRenderedReducer是 basicStateReducer
      // useState是一个特殊的useReducer 本质两个相同,区别就是采用的reducer不同
      // basicStateReducer 是react内部为state使用的reducer
      const lastRenderedReducer = queue.lastRenderedReducer;
      if (lastRenderedReducer !== null) {

        try {
          const currentState: S = (queue.lastRenderedState: any);
          // 计算的最新state
          const eagerState = lastRenderedReducer(currentState, action);

          update.eagerReducer = lastRenderedReducer;
          // 挂载在循环链表上
          update.eagerState = eagerState;
          if (is(eagerState, currentState)) {
                 /*
                前后两个state如果引用相同会直接return。 所以在react里数据的不可变性尤为重要
              假如直接的修改原state可能会暂时解决现在的问题,但是依赖state的后续节点还是会出问题
            */
            return;
          }
        } catch (error) {}

      }

      // 对于当前的fiber节点发起一次调度。
      // 可能会被批量更新优化调,也可能会被打断或者打断其他的更新
      scheduleUpdateOnFiber(fiber, lane, eventTime);
    }

   // 更新需要的时间相关
  if (enableSchedulingProfiler) {
    markStateUpdateScheduled(fiber, lane);
  }
}

updateState:


function updateReducer<S, I, A>(
  reducer: (S, A) => S,
  initialArg: I,
  init?: I => S,
): [S, Dispatch<A>] {

   // 获取更新时的下一个hook对象
  const hook = updateWorkInProgressHook();
  const queue = hook.queue;

    // reducer 对于state来说是basicStateReducer, 也就是默认的
  queue.lastRenderedReducer = reducer;

  const current: Hook = (currentHook: any);

    // 基本的更新队列
  let baseQueue = current.baseQueue;

    // 在dispatchAction中构建的更新循环链表
  const pendingQueue = queue.pending;
  if (pendingQueue !== null) {
    // 合并
    if (baseQueue !== null) {
      const baseFirst = baseQueue.next;
      const 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.
    const first = baseQueue.next;
    let newState = current.baseState;

    let newBaseState = null;
    let newBaseQueueFirst = null;
    let newBaseQueueLast = null;
    let update = first;
    do {
      const updateLane = update.lane;

      // 优先级不够,跳过本次更新
      if (!isSubsetOfLanes(renderLanes, updateLane)) {
        const clone: Update<S, A> = {
          lane: updateLane,
          action: update.action,
          eagerReducer: update.eagerReducer,
          eagerState: update.eagerState,
          next: (null: any),
        };
        if (newBaseQueueLast === null) {
          newBaseQueueFirst = newBaseQueueLast = clone;
          newBaseState = newState;
        } else {
          newBaseQueueLast = newBaseQueueLast.next = clone;
        }
        // 优先级不够,合并lane
        currentlyRenderingFiber.lanes = mergeLanes(
          currentlyRenderingFiber.lanes,
          updateLane,
        );
        markSkippedUpdateLanes(updateLane);
      } 
            // 执行本次更新
      else {
        if (newBaseQueueLast !== null) {
          // 将要被提交的更新, 不会被打断的更新
          const clone: Update<S, A> = {
            lane: NoLane,
            action: update.action,
            eagerReducer: update.eagerReducer,
            eagerState: update.eagerState,
            next: (null: any),
          };
          newBaseQueueLast = newBaseQueueLast.next = clone;
        }

        // 直接使用计算过的值, 也就是basicStateReducer计算过的值
        if (update.eagerReducer === reducer) {
          newState = ((update.eagerState: any): S);
        } else {
          const action = update.action;
          // 这个reducer就是useReducer(reducer)
          newState = reducer(newState, action);
        }
      }
      update = update.next;
    } while (update !== null && update !== first); // 循环链表

        // 如果更新完毕就返回最新的state
    if (newBaseQueueLast === null) {
      // 这里只有在本次更新产生的循环链表更新队列基于上一次的basestate更新完毕时才会被赋值为新的
      newBaseState = newState;
    } else {
      // 合并
      newBaseQueueLast.next = (newBaseQueueFirst: any);
    }
        // 两个state不同,重新更新
    if (!is(newState, hook.memoizedState)) {
      markWorkInProgressReceivedUpdate();
    }

    hook.memoizedState = newState;
    hook.baseState = newBaseState;
    // 将被执行完毕的链表节点保存
    hook.baseQueue = newBaseQueueLast;

    queue.lastRenderedState = newState;
  }

  const dispatch: Dispatch<A> = (queue.dispatch: any);
  return [hook.memoizedState, dispatch];
}

hook中的更新和setState的更新区别是,state不会被合并也就是没有使用Object.assagin()
setState的更新由updater对象提供。计算完成过state之后的更新和hook一致

on ✌️

let workInProgress = null, workInProgressRoot = null;
let workInProgressHook = null, firstWorkInProgressHook = null;

const currentHook = {
  current: null
};

function mountCurrentHook(initialiState) {
  const nextHook = {
    memoizedState: initialiState,
    baseState: initialiState,
    queue: null,
    next: null
  };

  // 首次挂载hook
  if (firstWorkInProgressHook === null) {
    firstWorkInProgressHook = workInProgressHook = nextHook;
  } else {
    workInProgressHook = workInProgressHook.next = nextHook;
  }

  return workInProgressHook;
}

function updateCurrentHook() {
  let currentAction = workInProgressHook.queue.pending;
  workInProgressHook.queue.pending = null;

  let nextState = workInProgressHook.memoizedState;

  if (currentAction !== null) {
    currentAction.next.next = null;

    while (currentAction !== null) {
      let nextAction = currentAction.action;

      if (typeof nextAction === "function") {
        nextState = nextAction(workInProgressHook.memoizedState);
      } else {
        nextState = nextAction;
      }

      currentAction = currentAction.next;
    }
  }

  workInProgressHook.memoizedState = nextState;

  let nextHook = workInProgressHook;

  if (firstWorkInProgressHook === workInProgressHook) {
    firstWorkInProgressHook = workInProgressHook;
  } else {
    workInProgressHook = workInProgressHook.next;
  }

  return nextHook;
}

export function render(el, callback) {

  const fiberRoot = {
    element: el,
    memoizedState: null,
    callback: callback
  };

  currentHook.current = mountCurrentHook;
  workInProgress = workInProgressRoot = fiberRoot;

  schedulerUpdateFiberOnRoot(fiberRoot);
}

function schedulerUpdateFiberOnRoot(current) {

    /* 理论上来说应该每次都从当前的fiber节点通过return指针找当应用的根节点,从根节点一次向下深度优先遍历。查找更新提交变更 */
    workInProgress = workInProgressRoot;
    workInProgressHook = firstWorkInProgressHook = workInProgress.memoizedState;

    let jsx /* --~ */ = workInProgress.element();
    workInProgress.memoizedState = firstWorkInProgressHook;
    workInProgressHook = firstWorkInProgressHook = null;
    workInProgress.callback(jsx)

    document.getElementById("root").textContent = JSON.stringify({ count: jsx.count }, null, 4)

    if (currentHook.current !== updateCurrentHook) {
      currentHook.current = updateCurrentHook;
    };
}


export function useState(initialiState) {
  const hook = currentHook.current(initialiState);
  const queue = hook.queue = {
    pending: null,
    dispatch: null
  };

  return [hook.memoizedState, dispatchAction.bind(null, queue, workInProgress)];
}


function dispatchAction(queue, fiber, action) {
  const update = {
    action,
    nextState: null,
    next: null
  };

  const pending = queue.pending;

  if (pending === null) {
    update.next = update;
  } else {
    // 插入一个新的udate对象,构成循环链表
    update.next = pending.next;
    pending.next = update;
  }

  queue.pending = update;

  schedulerUpdateFiberOnRoot(fiber);
}

之前写的,现在加上 我也忘了能不能运行