# react-hooks源码解析

react-hooks源码解析

参考资料

掘金用户:🍼holyZhengs

导语

hooks是function组件渲染中的一个环节。作用是将一部分组件的数据处理逻辑独立出来,以渐进式方式的加入到函数组件中。
源码地址packages/react-reconciler/src/ReactFiberHooks.js

根据源码实现简易hookDemo的代码地址:src/hookDemo目录下的CommonDemo组件

来玩局游戏吧!

废话不多说,上图:
在这里插入图片描述

  • 整个hooks执行阶段,可以类比成开一局游戏。
  • 准备阶段(投币开始):先重置游戏机(resetHooks)->投币(renderWithHooks)->创建游戏环境。
  • 执行阶段(游戏通关):开始游戏(Component)->按顺序通关(hooks)->通关成功输出战绩(虚拟dom节点)
  • hooks函数分为mountupdate两种模式。准备阶段时会判断fiber是否初次使用hooks来决定使用何种状态。

一、关卡(useState,useReducer)

useState需要解决的问题

  1. 作为一个公共函数,如何保证多个调用之间状态互不干扰?
    1. 以fiber为维度,每一个fiber保存一个hook链表(fiber.memoizedState)。以function中的执行顺序为依据,从保存的hook链表中获取状态
    2. useState执行于function组件中,而function组件在fiber树遍历时被调用。在function调用之前,react会先标记当前工作fiber。
    3. 每一个fiber有自己的hook链表,因为内部的hook钩子执行顺序不会被改变。hook的执行顺序将与fiber保存的hook链表顺序一一对应。以先进先出的方式执行
    4. 所以每一个hook钩子函数的执行,都能够正确的拿到对应的缓存状态,而不会相互干扰
// 创建hook并加入链表
function mountWorkInProgressHook(): Hook {
   
   const hook: Hook = {
   
      memoizedState: null,
      baseState: null,
      queue: null,
      baseUpdate: null,
      next: null,
   };
   if (workInProgressHook === null) {
   
      firstWorkInProgressHook = workInProgressHook = hook;
   } else {
   
      workInProgressHook = workInProgressHook.next = hook;
   }
   return workInProgressHook;
}     
// 更新组件时获取hook,可以用于更新阶段,也可以用于创建阶段
function updateWorkInProgressHook(): Hook {
   
   // 因为根据链表顺序获取hook,每执行一个钩子之前,先将下一个hook设置为活跃状态并返回
   if (nextWorkInProgressHook !== null) {
   
      workInProgressHook = nextWorkInProgressHook;
      nextWorkInProgressHook = workInProgressHook.next;
      currentHook = nextCurrentHook;
      nextCurrentHook = currentHook !== null ? currentHook.next : null;
   } else {
    // 如果没有匹配到对应的hook,则创建一个并加入到队列
      invariant(
         nextCurrentHook !== null,
         'Rendered more hooks than during the previous render.',
      );
      currentHook = nextCurrentHook;
      const newHook: Hook = {
   
         memoizedState: currentHook.memoizedState,
         baseState: currentHook.baseState,
         queue: currentHook.queue,
         baseUpdate: currentHook.baseUpdate,
         next: null,
      };
      if (workInProgressHook === null) {
   
         workInProgressHook = firstWorkInProgressHook = newHook;
      } else {
   
         workInProgressHook = workInProgressHook.next = newHook;
      }
     nextCurrentHook = currentHook.next;
   }
   return workInProgressHook;
}
  1. 多个useState如何保存?

    按执行顺序保存,多个钩子函数互不影响

  2. useState如何触发视图层更新,更新逻辑是怎样的?
    1. dispatchAction.bind(null, fiber, queue)执行时,会提交当前节点的变更。
    2. setState方法执行时,会标记当前节点的更新状态,并触发根节点更新

const [state, setState] = useState(null)做了什么?

const [state, setState] = useState(null)运行时可分为mountupdate两个阶段。这两个阶段在源码中对应着以下两个不同的useState方法。

mount阶段
  1. 创建一个钩子,并创建queue队列(缓存状态变更的队列)
  2. 变更方法绑定组件的fiber节点与queue队列,以关联组件
  3. 将初始化状态与变更方法返回给组件
  4. 源码分析:
function mountState<S>(
  initialState: (() => S) | S
): [S, Dispatch<BasicStateAction<S>>] {
   
  // 从钩子链表中获取当前执行的钩子
  const hook = mountWorkInProgressHook();
  // 获取传入的初始化参数
  if (typeof initialState === 'function') {
   
    initialState = initialState();
  }
  // 将历史状态与当前变更状态初始化
  hook.memoizedState = hook.baseState = initialState;
  // 创建储存状态变更的链表
  const queue = (hook.queue = {
   
    last: null,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: (initialState: any),
  });
  // 创建一个修改方法,并绑定到当前钩子。
  const dispatch: Dispatch<
    BasicStateAction<S>,
  > = (queue.dispatch = (dispatchAction.bind(
    null,
    // Flow doesn't know this is non-null, but we do.
    ((currentlyRenderingFiber: any): Fiber),
    queue,
  ): any));
  return [hook.memoizedState, dispatch];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值