likes
comments
collection
share

react hooks源码核心:workInProgressHook函数

作者站长头像
站长
· 阅读数 27

react hooks的更新核心,其实是一个链表的钩子。

目前可以确定,useRef和useState类似,每一个useState/useRef都可以理解为一个钩子,钩子(指针)存储在workInProgressHook中

可以回顾一下useRef的源码:react hooks本质探索 - useRef源码详解

初始化workInProgressHook代码如下(中文注释为笔者添加的注释,英文注释为源代码里的注释)

function mountWorkInProgressHook() {
  // 新建链表结点
  var hook = {
    memoizedState: null,
    baseState: null,
    baseQueue: null,
    queue: null,
    next: null
  };
  
  // workInProgressHook可以理解为链表的指针
  // 一般情况:指针指的不是链表的开头
  if (workInProgressHook !== null) {
    // 把新的hook赋值给当前workInProgressHook的next
    workInProgressHook.next = hook;
    
    // 让当前的指针指向下一个钩子
    workInProgressHook = workInProgressHook.next
  }

  // 特殊情况:指针指的是null,说明链表未建立。
  if (workInProgressHook === null) {
     // 让指针指向hook  
     workInProgressHook = hook;
     
     // 这里先不去管他,currentlyRenderingFiber$1应该是优化渲染部分的
     currentlyRenderingFiber$1.memoizedState = workInProgressHook;
  }

  // 返回指针
  return workInProgressHook;
}

总结来说,等于是把链表的next指向一个新的hook,同时返回了这个链表的表尾。以上就是初始化useRef/useState干的事情。

接下来看更新时,useRef/useState干了什么:因为代码比较长,可以先看一个步骤图了解一下步骤拆解

react hooks源码核心:workInProgressHook函数

下面看源码(中文注释为笔者添加的注释,英文注释为源代码里的注释):

function updateWorkInProgressHook() {
  // This function is used both for updates and for re-renders triggered by a
  // render phase update. It assumes there is either a current hook we can
  // clone, or a work-in-progress hook from a previous render pass that we can
  // use as a base. When we reach the end of the base list, we must switch to
  // the dispatcher used for mounts.
  var nextCurrentHook;

   // 这里在workInProgressHook之外,引入了一个新的概念:currentHook
  // 但是这个概念可以不用管,因为这是fiber的概念。我们本次不需要知道fiber的概念 
  if (currentHook === null) {
    var current = currentlyRenderingFiber$1.alternate;

    if (current !== null) {
      nextCurrentHook = current.memoizedState;
    } else {
      nextCurrentHook = null;
    }
  } else {
    nextCurrentHook = currentHook.next;
  }

  // 来到我们熟悉的workInProgressHook
  var nextWorkInProgressHook;

  // 以下这块代码,是为了初始化nextWorkInProgressHook
  // 特殊情况:链表开头。如果当前的指针指向为空,说明为开头。注意这里判断的是workInProgressHook
  if (workInProgressHook === null) {
    // 为什么这么赋值,也跟fiber有关。可以先不管  
    nextWorkInProgressHook = currentlyRenderingFiber$1.memoizedState;
  } else {
    // 如果不是链表开头,那么会让nextWorkInProgressHook等于当前链表指针的next,很好理解
    nextWorkInProgressHook = workInProgressHook.next;
  }

  // 这个模块是为了让workInProgressHook正常地指向下一个链表节点
  // 正常情况:初始化nextWorkInProgressHook后,nextWorkInProgressHook不为空
  if (nextWorkInProgressHook !== null) {
    // 这部分逻辑就是正常将workInProgressHook指针指向下一个元素
    // There's already a work-in-progress. Reuse it.
    workInProgressHook = nextWorkInProgressHook;
    
    // nextWorkInProgressHook在这个方法里不再用到,但是一个全局变量,其他地方可能用到。
    nextWorkInProgressHook = workInProgressHook.next;
    currentHook = nextCurrentHook;
  } else {
    // 特殊情况:初始化nextWorkInProgressHook后,nextWorkInProgressHook仍然为空
    // 可能有两种情况:
    //   1.workInProgressHook为空(代表链表为空),currentlyRenderingFiber$1.memoizedState为空
    //   2.workInProgressHook不为空,当前指针指向了链表的尾部,所以workInProgressHook.next为空,导致nextWorkInProgressHook为空。
    // 不管链表为空,还是指向了结尾,抽象意义上都是指向了链表结尾。
    // 这两种情况,最后都执行了workInProgressHook = newHook;
        
    // 这一块都是处理fiberNode,可以不管
    // Clone from the current hook.
    if (!(nextCurrentHook !== null)) {
      {
        throw Error( "Rendered more hooks than during the previous render." );
      }
    }

    currentHook = nextCurrentHook;
    var newHook = {
      memoizedState: currentHook.memoizedState,
      baseState: currentHook.baseState,
      baseQueue: currentHook.baseQueue,
      queue: currentHook.queue,
      next: null
    };

    // 处理链表为空的情况
    // 这里要注意,如果nextWorkInProgressHook不为空,那么workInProgressHook也一定不为空。所以这里隐含了一个条件:
    // workInProgressHook和nextWorkInProgressHook都为空。
    if (workInProgressHook === null) {
      // 这里稍稍改写下,把连续赋值分成了两部分
      
      // 链表为空,就需要把第一个元素赋值给指针,让链表指针正常
      // This is the first hook in the list.
      workInProgressHook = newHook;
      
      // fiber相关
      currentlyRenderingFiber$1.memoizedState = workInProgressHook;
    } else {
    // 当前指针指向了链表的尾部的情况
    
      // 这里做了简单的改写
      // Append to the end of the list.
      workInProgressHook.next = newHook;
      
      // 可以看到,workInProgressHook = newHook是必定执行的。也要注意,这里是在两个else里。
      workInProgressHook = newHook;
    }
  }

  return workInProgressHook;
}