react Hooks(useRef、useMemo、useCallback)实现原理

Fiber

上篇文章fiber简单理解记录了react fiber架构,Hooks是基于fiber链表来实现的。阅读以下内容时建议先了解react fiber。

jsx -> render function -> vdom -> fiber树 -> dom

vdom 转 fiber 的过程称为 recocile。diff算法就是在recocile这个过程中实现。

经过 reconcile 生成新的fiber树。这时候还没有处理副作用(hooks、生命周期等),这些会在reconcile 结束之后处理。

react渲染分为两个阶段:render 和 commit。

render:指 vdom -> fiber 的过程

commit: 指 fiber -> dom 具体操作dom以及执行副作用函数的过程。其包含了三个小阶段:before mutation、mutation(真实操作dom)、 layout。

Hooks

hooks是在哪个阶段处理呢? commit fiber -> dom

hooks的数据保存在哪里呢? fiber 的 memoizedState 链表上存取数据。

是什么时候构造这个链表呢? 在第一次调用 useXxx 的时候

一、 useRef

语法:const refContainer = useRef(initialValue);

作用:useRef 返回一个可变的 ref 对象,其内部只有一个 current 属性被初始化为传入的参数(initialValue)

useRef是怎么实现的呢?

第一次 调用 useRef 会走到 mountRef:

在 mountRef 里可以看到它创建了一个 hook 节点,然后设置了 memoizedState 属性为有 current 属性的对象,也就是 ref 对象。

js 复制代码
//以下代码为源码中相关代码缩写,详细过程请看源码
function mountRef<T>(initialValue: T): {current: T} {
  const hook = mountWorkInProgressHook();
  const ref = {current: initialValue};
  hook.memoizedState = ref;
  return ref
}

mountWorkInProgressHook函数用来具体创建 hook 链表

其第一个节点挂在 fiber 的 memoizedState 属性上,后面的挂在上个节点的 next 属性上。

第二次 调用 useRef 会走到 updateRef:

这里的 updateRef 就是取出 hook 的 momorizedState 的值直接返回了:

所以 useRef 的返回的 ref 对象始终是最开始那个。

二、useMemo

语法: const cachedValue = useMemo(calculateValue, dependencies)

作用: 它在每次重新渲染的时候能够缓存计算的结果。当依赖的值不变时始终返回之前的值,依赖值变化时创建新的值

那它是怎么实现的呢?

useMemo 同样也是分为 mountMemo 和 updateMemo 两个阶段。

第一次调用 useMemo 会执行 mountMemo

创建 hook, 执行传入的 nextCreate 函数,把值设置到 hook.memoizedState 属性上。

更新数据时执行的是 updateMemo

在 updateMemo 的实现中,有一个关键函数 areHookInputsEqual,它用于比较依赖项数组:

如果依赖数组都没变,那就返回之前的值,否则创建新的值更新到 hook.memoizedState。

三、useCallback

语法: const cachedFn = useCallback(fn, dependencies)

作用: 是一个允许你在多次渲染中缓存函数的 React Hook。当依赖的值变化时才执行fn。

那它是怎么实现的呢?

useCallback 同样也是分为 mountCallback 和 updateCallback 两个阶段。

第一次调用 mountCallback

第二次及以后updateCallback

其实现和 useMemo 基本一致。其原理都是通过areHookInputsEqual 函数进行依赖项比对,区别在于 useMemo 返回是新数据对象,而 useCallback 返回是回调函数

小结: hook 的数据存放在 fiber 的 memoizedState 属性的链表上,每个 hook 对应一个节点,第一次执行 useXxx 的 hook 会走 mountXxx 的逻辑来创建 hook 链表,之后会走 updateXxx 的逻辑。

当然,前面的 useRef、useCallback、useMemo 都相对简单。但 useEffect 和 useState 就相对复杂了。考虑到篇幅问题,useEffect 详情记录在写一篇文章中。

参考来源:
reacthooks源码
react中文文档

相关推荐
泯泷2 小时前
手搓JSVMM第 8 篇:函数调用:参数、返回值与调用帧
前端·javascript·前端框架
Sherotree2 小时前
Google Antigravity——Agent 被提到主界面
前端·ide·后端·edge浏览器
里欧跑得慢3 小时前
Flutter 像素级还原:设计稿到代码的视觉无损转换技巧
前端·css·flutter·web
里欧跑得慢3 小时前
Flutter 三方库 function_tree — 鸿蒙应用开发中的动态数学公式解析与计算神器,实现鸿蒙深度适配下的复杂函数逻辑运行时求值实战(适配鸿蒙 HarmonyOS Next ohos)
android·前端·安全·flutter·华为·harmonyos
苏灿烤鱼3 小时前
没有机密文件,也没有付费数据,在浏览器里造一颗"间谍卫星"
前端·javascript·github
泯泷4 小时前
手搓JSVM第 5 篇:写第一个编译器:从 AST 生成 IR
前端·javascript·算法
泯泷4 小时前
手搓JSVM第 7 篇:控制流:if、while 与 jump
前端·javascript·算法
泯泷4 小时前
手搓JSVM第 6 篇:把 IR 编成字节码:emit 与 label fixup
前端·javascript·算法
泯泷4 小时前
手搓JSVM第 3 篇:从栈式 VM 到寄存器式 VM:为什么我们选择寄存器
前端·javascript·算法
泯泷4 小时前
第 4 篇:让 VM 支持变量:Slot、Environment 与 TDZ
前端·javascript·算法