IWSR / react-code-debug

react v18 源码分析
7 stars 1 forks source link

React Hooks: 附录 #4

Open IWSR opened 2 years ago

IWSR commented 2 years ago

附录

该文件内对 Hooks 内一些常见的函数/概念做解析

updateWorkInProgressHook

功能:同时移动 currentHook 与 workInProgressHook 这两个指针

let currentHook: Hook | null = null;
let workInProgressHook: Hook | null = null;
...
function updateWorkInProgressHook(): Hook {
  // 1. 移动 currentHook 指针
  let nextCurrentHook: null | Hook;
  /**
   * currentHook / workInProgressHook 在初始化的时候都是null
  */
  if (currentHook === null) {
    const current = currentlyRenderingFiber.alternate; // 获取当前 wip fiber 对应的 current fiber 节点
    if (current !== null) {
      nextCurrentHook = current.memoizedState; // hooks 链表存储在 fiber 节点的 memoizedState 属性上
    } else {
      nextCurrentHook = null; // current fiber 节点为空,那也不可能存在对应的 hooks 链表
    }
  } else {
    nextCurrentHook = currentHook.next; // 指针后移
  }

  let nextWorkInProgressHook: null | Hook;
  /**
   * 如果 WIP 的 hooks 为空,就把 WIP 上的 hooks 链表赋予它
   * 否则 指针后移
  */
  if (workInProgressHook === null) {
    nextWorkInProgressHook = currentlyRenderingFiber.memoizedState;
  } else {
    nextWorkInProgressHook = workInProgressHook.next;
  }

  if (nextWorkInProgressHook !== null) {
    // There's already a work-in-progress. Reuse it.
    // 已经存在了 WIP 的 hooks 节点,复用即可
    workInProgressHook = nextWorkInProgressHook;
    nextWorkInProgressHook = workInProgressHook.next;

    currentHook = nextCurrentHook;
  } else {
    // Clone from the current hook.
    // WIP hooks 的数量多于 current hooks 的数量,直接报错
    // 因此不允许通过条件判断包裹 hooks,必须保证每次调用的 hooks 数量一致
    if (nextCurrentHook === null) {
      throw new Error('Rendered more hooks than during the previous render.');
    }

    /**
     * 借 currentHook 生成新的 workInProgressHook
    */
    currentHook = nextCurrentHook;

    const newHook: Hook = {
      memoizedState: currentHook.memoizedState,

      baseState: currentHook.baseState,
      baseQueue: currentHook.baseQueue,
      queue: currentHook.queue,

      next: null,
    };
    /**
     * 连接到 WIP Hook 的链表后面
    */
    if (workInProgressHook === null) {
      // This is the first hook in the list.
      currentlyRenderingFiber.memoizedState = workInProgressHook = newHook;
    } else {
      // Append to the end of the list.
      workInProgressHook = workInProgressHook.next = newHook;
    }
  }
  return workInProgressHook;
}