• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

React源码(十三) Hook 原理(概览)

武飞扬头像
优价实习
帮助2

Hook 原理(概览)

在前文状态与副作用中, 总结了class组件, function组件中通过api去改变fiber节点状态副作用. 其中对于function组件来讲, 其内部则需要依靠Hook来实现.

官方文档上专门用了一个版块来介绍Hook, 这里摘抄了几个比较关心的问题(其他FAQ请移步官网):

  1. 引入Hook的动机?

    • 在组件之间复用状态逻辑很难; 复杂组件变得难以理解; 难以理解的 class. 为了解决这些实际开发痛点, 引入了Hook.
  2. Hook 是什么? 什么时候会用 Hook?

    • Hook 是一个特殊的函数, 它可以让你“钩入” React 的特性. 如, useState 是允许你在 React 函数组件中添加 state 的 Hook.
    • 如果你在编写函数组件并意识到需要向其添加一些 state, 以前的做法是必须将其转化为 class. 现在你可以在现有的函数组件中使用 Hook.
  3. Hook 会因为在渲染时创建函数而变慢吗?

    • 不会. 在现代浏览器中,闭包和类的原始性能只有在极端场景下才会有明显的差别. 除此之外,可以认为 Hook 的设计在某些方面更加高效:
      • Hook 避免了 class 需要的额外开支,像是创建类实例和在构造函数中绑定事件处理器的成本.
      • 符合语言习惯的代码在使用 Hook 时不需要很深的组件树嵌套. 这个现象在使用高阶组件render props、和 context 的代码库中非常普遍. 组件树小了, React 的工作量也随之减少.

所以HookReact团队在大量实践后的产物, 更优雅的代替class, 且性能更高. 故从开发使用者的角度来讲, 应该拥抱Hook所带来的便利.

Hook 与 Fiber

通过官网文档的讲解, 能快速掌握Hook的使用. 再结合前文状态与副作用的介绍, 我们知道使用Hook最终也是为了控制fiber节点状态副作用. 从fiber视角, 状态和副作用相关的属性如下(这里不再解释单个属性的意义, 可以回顾状态与副作用):

  1.  
    export type Fiber = {|
  2.  
     
  3.  
    // 1. fiber节点自身状态相关
  4.  
     
  5.  
    pendingProps: any,
  6.  
     
  7.  
    memoizedProps: any,
  8.  
     
  9.  
    updateQueue: mixed,
  10.  
     
  11.  
    memoizedState: any,
  12.  
     
  13.  
    // 2. fiber节点副作用(Effect)相关
  14.  
     
  15.  
    flags: Flags,
  16.  
     
  17.  
    nextEffect: Fiber | null,
  18.  
     
  19.  
    firstEffect: Fiber | null,
  20.  
     
  21.  
    lastEffect: Fiber | null,
  22.  
     
  23.  
    |};
学新通

使用Hook的任意一个api, 最后都是为了控制上述这几个fiber属性.

Hook 数据结构

ReactFiberHooks中, 定义了Hook的数据结构:

  1.  
    type Update<S, A> = {|
  2.  
     
  3.  
    lane: Lane,
  4.  
     
  5.  
    action: A,
  6.  
     
  7.  
    eagerReducer: ((S, A) => S) | null,
  8.  
     
  9.  
    eagerState: S | null,
  10.  
     
  11.  
    next: Update<S, A>,
  12.  
     
  13.  
    priority?: ReactPriorityLevel,
  14.  
     
  15.  
    |};
  16.  
     
  17.  
    type UpdateQueue<S, A> = {|
  18.  
     
  19.  
    pending: Update<S, A> | null,
  20.  
     
  21.  
    dispatch: (A => mixed) | null,
  22.  
     
  23.  
    lastRenderedReducer: ((S, A) => S) | null,
  24.  
     
  25.  
    lastRenderedState: S | null,
  26.  
     
  27.  
    |};
  28.  
     
  29.  
    export type Hook = {|
  30.  
     
  31.  
    memoizedState: any, // 当前状态
  32.  
     
  33.  
    baseState: any, // 基状态
  34.  
     
  35.  
    baseQueue: Update<any, any> | null, // 基队列
  36.  
     
  37.  
    queue: UpdateQueue<any, any> | null, // 更新队列
  38.  
     
  39.  
    next: Hook | null, // next指针
  40.  
     
  41.  
    |};
学新通

从定义来看, Hook对象共有 5 个属性(有关这些属性的应用, 将在Hook 原理(状态)章节中具体分析.):

  1. hook.memoizedState: 保持在内存中的局部状态.
  2. hook.baseStatehook.baseQueue中所有update对象合并之后的状态.
  3. hook.baseQueue: 存储update对象的环形链表, 只包括高于本次渲染优先级的update对象.
  4. hook.queue: 存储update对象的环形链表, 包括所有优先级的update对象.
  5. hook.nextnext指针, 指向链表中的下一个hook.

所以Hook是一个链表, 单个Hook拥有自己的状态hook.memoizedState和自己的更新队列hook.queue(有关 Hook 状态的分析, 在Hook原理(状态)章节中解读).

学新通

注意: 其中hook.queuefiber.updateQueue虽然都是update环形链表, 尽管update对象的数据结构与处理方式都高度相似, 但是这 2 个队列中的update对象是完全独立的. hook.queue只作用于hook对象的状态维护, 切勿与fiber.updateQueue混淆.

Hook 分类

v17.0.2中, 共定义了14 种 Hook

  1.  
    export type HookType =
  2.  
     
  3.  
    | 'useState'
  4.  
     
  5.  
    | 'useReducer'
  6.  
     
  7.  
    | 'useContext'
  8.  
     
  9.  
    | 'useRef'
  10.  
     
  11.  
    | 'useEffect'
  12.  
     
  13.  
    | 'useLayoutEffect'
  14.  
     
  15.  
    | 'useCallback'
  16.  
     
  17.  
    | 'useMemo'
  18.  
     
  19.  
    | 'useImperativeHandle'
  20.  
     
  21.  
    | 'useDebugValue'
  22.  
     
  23.  
    | 'useDeferredValue'
  24.  
     
  25.  
    | 'useTransition'
  26.  
     
  27.  
    | 'useMutableSource'
  28.  
     
  29.  
    | 'useOpaqueIdentifier';
学新通

官网上已经将其分为了 2 个类别, 状态Hook(State Hook), 和副作用Hook(Effect Hook).

这里我们可以结合前文状态与副作用, 从fiber的视角去理解状态Hook副作用Hook的区别.

状态 Hook

狭义上讲, useState, useReducer可以在function组件添加内部的state, 且useState实际上是useReducer的简易封装, 是一个最特殊(简单)的useReducer. 所以将useState, useReducer称为状态Hook.

广义上讲, 只要能实现数据持久化且没有副作用Hook, 均可以视为状态Hook, 所以还包括useContext, useRef, useCallback, useMemo等. 这类Hook内部没有使用useState/useReducer, 但是它们也能实现多次render时, 保持其初始值不变(即数据持久化)且没有任何副作用.

得益于双缓冲技术(double buffering), 在多次render时, 以fiber为载体, 保证复用同一个Hook对象, 进而实现数据持久化. 具体实现细节, 在Hook原理(状态)章节中讨论.

副作用 Hook

回到fiber视角, 状态Hook实现了状态持久化(等同于class组件维护fiber.memoizedState), 那么副作用Hook则会修改fiber.flags. (通过前文fiber树构造系列的解读, 我们知道在performUnitOfWork->completeWork阶段, 所有存在副作用的fiber节点, 都会被添加到父节点的副作用队列后, 最后在commitRoot阶段处理这些副作用节点.)

另外, 副作用Hook还提供了副作用回调(类似于class组件的生命周期回调), 比如:

  1.  
    // 使用useEffect时, 需要传入一个副作用回调函数.
  2.  
     
  3.  
    // 在fiber树构造完成之后, commitRoot阶段会处理这些副作用回调
  4.  
     
  5.  
    useEffect(() => {
  6.  
     
  7.  
    console.log('这是一个副作用回调函数');
  8.  
     
  9.  
    }, []);

react内部, useEffect就是最标准的副作用Hook. 其他比如useLayoutEffect以及自定义Hook, 如果要实现副作用, 必须直接或间接的调用useEffect.

有关useEffect具体实现细节, 在Hook原理(副作用)章节中讨论.

组合 Hook

虽然官网并无组合Hook的说法, 但事实上大多数Hook(包括自定义Hook)都是由上述 2 种 Hook组合而成, 同时拥有这 2 种 Hook 的特性.

  • react内部有useDeferredValue, useTransition, useMutableSource, useOpaqueIdentifier等.
  • 平时开发中, 自定义Hook大部分都是组合 Hook.

比如官网上的自定义 Hook例子:

  1.  
    import { useState, useEffect } from 'react';
  2.  
     
  3.  
    function useFriendStatus(friendID) {
  4.  
     
  5.  
    // 1. 调用useState, 创建一个状态Hook
  6.  
     
  7.  
    const [isOnline, setIsOnline] = useState(null);
  8.  
     
  9.  
    // 2. 调用useEffect, 创建一个副作用Hook
  10.  
     
  11.  
    useEffect(() => {
  12.  
     
  13.  
    function handleStatusChange(status) {
  14.  
     
  15.  
    setIsOnline(status.isOnline);
  16.  
     
  17.  
    }
  18.  
     
  19.  
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
  20.  
     
  21.  
    return () => {
  22.  
     
  23.  
    ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
  24.  
     
  25.  
    };
  26.  
     
  27.  
    });
  28.  
     
  29.  
    return isOnline;
  30.  
     
  31.  
    }
学新通

调用 function 前

在调用function之前, react内部还需要提前做一些准备工作.

处理函数

fiber树构造的视角来看, 不同的fiber类型, 只需要调用不同的处理函数返回fiber子节点. 所以在performUnitOfWork->beginWork函数中, 调用了多种处理函数. 从调用方来讲, 无需关心处理函数的内部实现(比如updateFunctionComponent内部使用了Hook对象updateClassComponent内部使用了class实例).

本节讨论Hook, 所以列出其中的updateFunctionComponent函数:

  1.  
    // 只保留FunctionComponent相关:
  2.  
     
  3.  
    function beginWork(
  4.  
     
  5.  
    current: Fiber | null,
  6.  
     
  7.  
    workInProgress: Fiber,
  8.  
     
  9.  
    renderLanes: Lanes,
  10.  
     
  11.  
    ): Fiber | null {
  12.  
     
  13.  
    const updateLanes = workInProgress.lanes;
  14.  
     
  15.  
    switch (workInProgress.tag) {
  16.  
     
  17.  
    case FunctionComponent: {
  18.  
     
  19.  
    const Component = workInProgress.type;
  20.  
     
  21.  
    const unresolvedProps = workInProgress.pendingProps;
  22.  
     
  23.  
    const resolvedProps =
  24.  
     
  25.  
    workInProgress.elementType === Component
  26.  
     
  27.  
    ? unresolvedProps
  28.  
     
  29.  
    : resolveDefaultProps(Component, unresolvedProps);
  30.  
     
  31.  
    return updateFunctionComponent(
  32.  
     
  33.  
    current,
  34.  
     
  35.  
    workInProgress,
  36.  
     
  37.  
    Component,
  38.  
     
  39.  
    resolvedProps,
  40.  
     
  41.  
    renderLanes,
  42.  
     
  43.  
    );
  44.  
     
  45.  
    }
  46.  
     
  47.  
    }
  48.  
     
  49.  
    }
  50.  
     
  51.  
    function updateFunctionComponent(
  52.  
     
  53.  
    current,
  54.  
     
  55.  
    workInProgress,
  56.  
     
  57.  
    Component,
  58.  
     
  59.  
    nextProps: any,
  60.  
     
  61.  
    renderLanes,
  62.  
     
  63.  
    ) {
  64.  
     
  65.  
    // ...省略无关代码
  66.  
     
  67.  
    let context;
  68.  
     
  69.  
    let nextChildren;
  70.  
     
  71.  
    prepareToReadContext(workInProgress, renderLanes);
  72.  
     
  73.  
    // 进入Hooks相关逻辑, 最后返回下级ReactElement对象
  74.  
     
  75.  
    nextChildren = renderWithHooks(
  76.  
     
  77.  
    current,
  78.  
     
  79.  
    workInProgress,
  80.  
     
  81.  
    Component,
  82.  
     
  83.  
    nextProps,
  84.  
     
  85.  
    context,
  86.  
     
  87.  
    renderLanes,
  88.  
     
  89.  
    );
  90.  
     
  91.  
    // 进入reconcile函数, 生成下级fiber节点
  92.  
     
  93.  
    reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  94.  
     
  95.  
    // 返回下级fiber节点
  96.  
     
  97.  
    return workInProgress.child;
  98.  
     
  99.  
    }
学新通

updateFunctionComponent函数中调用了renderWithHooks(位于ReactFiberHooks) , 至此FiberHook产生了关联.

全局变量

在分析renderWithHooks函数前, 有必要理解ReactFiberHooks头部定义的全局变量(源码中均有英文注释):

  1.  
    // 渲染优先级
  2.  
     
  3.  
    let renderLanes: Lanes = NoLanes;
  4.  
     
  5.  
    // 当前正在构造的fiber, 等同于 workInProgress, 为了和当前hook区分, 所以将其改名
  6.  
     
  7.  
    let currentlyRenderingFiber: Fiber = (null: any);
  8.  
     
  9.  
    // Hooks被存储在fiber.memoizedState 链表上
  10.  
     
  11.  
    let currentHook: Hook | null = null; // currentHook = fiber(current).memoizedState
  12.  
     
  13.  
    let workInProgressHook: Hook | null = null; // workInProgressHook = fiber(workInProgress).memoizedState
  14.  
     
  15.  
    // 在function的执行过程中, 是否再次发起了更新. 只有function被完全执行之后才会重置.
  16.  
     
  17.  
    // 当render异常时, 通过该变量可以决定是否清除render过程中的更新.
  18.  
     
  19.  
    let didScheduleRenderPhaseUpdate: boolean = false;
  20.  
     
  21.  
    // 在本次function的执行过程中, 是否再次发起了更新. 每一次调用function都会被重置
  22.  
     
  23.  
    let didScheduleRenderPhaseUpdateDuringThisPass: boolean = false;
  24.  
     
  25.  
    // 在本次function的执行过程中, 重新发起更新的最大次数
  26.  
     
  27.  
    const RE_RENDER_LIMIT = 25;
学新通

每个变量的解释, 可以对照源码中的英文注释, 其中最重要的有:

  1. currentlyRenderingFiber: 当前正在构造的 fiber, 等同于 workInProgress
  2. currentHook 与 workInProgressHook: 分别指向current.memoizedStateworkInProgress.memoizedState

注: 有关currentworkInProgress的区别, 请回顾双缓冲技术(double buffering)

renderWithHooks 函数

renderWithHooks源码看似较长, 但是去除 dev 后保留主干, 逻辑十分清晰. 以调用function为分界点, 逻辑被分为 3 个部分:

  1.  
     
  2.  
    // ...省略无关代码
  3.  
     
  4.  
    export function renderWithHooks<Props, SecondArg>(
  5.  
     
  6.  
    current: Fiber | null,
  7.  
     
  8.  
    workInProgress: Fiber,
  9.  
     
  10.  
    Component: (p: Props, arg: SecondArg) => any,
  11.  
     
  12.  
    props: Props,
  13.  
     
  14.  
    secondArg: SecondArg,
  15.  
     
  16.  
    nextRenderLanes: Lanes,
  17.  
     
  18.  
    ): any {
  19.  
     
  20.  
    // --------------- 1. 设置全局变量 -------------------
  21.  
     
  22.  
    renderLanes = nextRenderLanes; // 当前渲染优先级
  23.  
     
  24.  
    currentlyRenderingFiber = workInProgress; // 当前fiber节点, 也就是function组件对应的fiber节点
  25.  
     
  26.  
    // 清除当前fiber的遗留状态
  27.  
     
  28.  
    workInProgress.memoizedState = null;
  29.  
     
  30.  
    workInProgress.updateQueue = null;
  31.  
     
  32.  
    workInProgress.lanes = NoLanes;
  33.  
     
  34.  
    // --------------- 2. 调用function,生成子级ReactElement对象 -------------------
  35.  
     
  36.  
    // 指定dispatcher, 区分mount和update
  37.  
     
  38.  
    ReactCurrentDispatcher.current =
  39.  
     
  40.  
    current === null || current.memoizedState === null
  41.  
     
  42.  
    ? HooksDispatcherOnMount
  43.  
     
  44.  
    : HooksDispatcherOnUpdate;
  45.  
     
  46.  
    // 执行function函数, 其中进行分析Hooks的使用
  47.  
     
  48.  
    let children = Component(props, secondArg);
  49.  
     
  50.  
    // --------------- 3. 重置全局变量,并返回 -------------------
  51.  
     
  52.  
    // 执行function之后, 还原被修改的全局变量, 不影响下一次调用
  53.  
     
  54.  
    renderLanes = NoLanes;
  55.  
     
  56.  
    currentlyRenderingFiber = (null: any);
  57.  
     
  58.  
    currentHook = null;
  59.  
     
  60.  
    workInProgressHook = null;
  61.  
     
  62.  
    didScheduleRenderPhaseUpdate = false;
  63.  
     
  64.  
    return children;
  65.  
     
  66.  
    }
学新通
  1. 调用function前: 设置全局变量, 标记渲染优先级和当前fiber, 清除当前fiber的遗留状态.
  2. 调用function: 构造出Hooks链表, 最后生成子级ReactElement对象(children).
  3. 调用function后: 重置全局变量, 返回children.
    • 为了保证不同的function节点在调用时renderWithHooks互不影响, 所以退出时重置全局变量.

调用 function

Hooks 构造

function中, 如果使用了Hook api(如: useEffectuseState), 就会创建一个与之对应的Hook对象, 接下来重点分析这个创建过程.

有如下 demo:hook-summary - CodeSandbox

12

function组件中, 同时使用了状态Hook副作用Hook.

初次渲染时, 逻辑执行到performUnitOfWork->beginWork->updateFunctionComponent->renderWithHooks前, 内存结构如下(本节重点是Hook, 有关fiber树构造过程可回顾前文):

学新通

当执行renderWithHooks时, 开始调用function. 本例中, 在function内部, 共使用了 4 次Hook api, 依次调用useState, useEffect, useState, useEffect.

useState, useEffectfiber初次构造时分别对应mountStatemountEffect->mountEffectImpl

  1.  
    function mountState<S>(
  2.  
     
  3.  
    initialState: (() => S) | S,
  4.  
     
  5.  
    ): [S, Dispatch<BasicStateAction<S>>] {
  6.  
     
  7.  
    const hook = mountWorkInProgressHook();
  8.  
     
  9.  
    // ...省略部分本节不讨论
  10.  
     
  11.  
    return [hook.memoizedState, dispatch];
  12.  
     
  13.  
    }
  14.  
     
  15.  
    function mountEffectImpl(fiberFlags, hookFlags, create, deps): void {
  16.  
     
  17.  
    const hook = mountWorkInProgressHook();
  18.  
     
  19.  
    // ...省略部分本节不讨论
  20.  
     
  21.  
    }
学新通

无论useState, useEffect, 内部都通过mountWorkInProgressHook创建一个 hook.

链表存储

mountWorkInProgressHook非常简单:

f

  1.  
    unction mountWorkInProgressHook(): Hook {
  2.  
     
  3.  
    const hook: Hook = {
  4.  
     
  5.  
    memoizedState: null,
  6.  
     
  7.  
    baseState: null,
  8.  
     
  9.  
    baseQueue: null,
  10.  
     
  11.  
    queue: null,
  12.  
     
  13.  
    next: null,
  14.  
     
  15.  
    };
  16.  
     
  17.  
    if (workInProgressHook === null) {
  18.  
     
  19.  
    // 链表中首个hook
  20.  
     
  21.  
    currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
  22.  
     
  23.  
    } else {
  24.  
     
  25.  
    // 将hook添加到链表末尾
  26.  
     
  27.  
    workInProgressHook = workInProgressHook.next = hook;
  28.  
     
  29.  
    }
  30.  
     
  31.  
    return workInProgressHook;
  32.  
     
  33.  
    }
学新通

逻辑是创建Hook并挂载到fiber.memoizedState上, 多个Hook以链表结构保存.

本示例中, function调用之后则会创建 4 个hook, 这时的内存结构如下:

学新通

可以看到: 无论状态Hook副作用Hook都按照调用顺序存储在fiber.memoizedState链表中.

学新通

顺序克隆

fiber树构造(对比更新)阶段, 执行updateFunctionComponent->renderWithHooks时再次调用function调用function前的内存结构如下:

学新通

注意: 在renderWithHooks函数中已经设置了workInProgress.memoizedState = null, 等待调用function时重新设置.

接下来调用function, 同样依次调用useState, useEffect, useState, useEffect. 而useState, useEffectfiber对比更新时分别对应updateState->updateReducerupdateEffect->updateEffectImpl

无论useState, useEffect, 内部调用updateWorkInProgressHook获取一个 hook.

  1.  
    function updateWorkInProgressHook(): Hook {
  2.  
     
  3.  
    // 1. 移动currentHook指针
  4.  
     
  5.  
    let nextCurrentHook: null | Hook;
  6.  
     
  7.  
    if (currentHook === null) {
  8.  
     
  9.  
    const current = currentlyRenderingFiber.alternate;
  10.  
     
  11.  
    if (current !== null) {
  12.  
     
  13.  
    nextCurrentHook = current.memoizedState;
  14.  
     
  15.  
    } else {
  16.  
     
  17.  
    nextCurrentHook = null;
  18.  
     
  19.  
    }
  20.  
     
  21.  
    } else {
  22.  
     
  23.  
    nextCurrentHook = currentHook.next;
  24.  
     
  25.  
    }
  26.  
     
  27.  
     
  28.  
     
  29.  
    // 2. 移动workInProgressHook指针
  30.  
     
  31.  
    let nextWorkInProgressHook: null | Hook;
  32.  
     
  33.  
    if (workInProgressHook === null) {
  34.  
     
  35.  
    nextWorkInProgressHook = currentlyRenderingFiber.memoizedState;
  36.  
     
  37.  
    } else {
  38.  
     
  39.  
    nextWorkInProgressHook = workInProgressHook.next;
  40.  
     
  41.  
    }
  42.  
     
  43.  
    if (nextWorkInProgressHook !== null) {
  44.  
     
  45.  
    // 渲染时更新: 本节不讨论
  46.  
     
  47.  
    } else {
  48.  
     
  49.  
    currentHook = nextCurrentHook;
  50.  
     
  51.  
    // 3. 克隆currentHook作为新的workInProgressHook.
  52.  
     
  53.  
    // 随后逻辑与mountWorkInProgressHook一致
  54.  
     
  55.  
    const newHook: Hook = {
  56.  
     
  57.  
    memoizedState: currentHook.memoizedState,
  58.  
     
  59.  
    baseState: currentHook.baseState,
  60.  
     
  61.  
    baseQueue: currentHook.baseQueue,
  62.  
     
  63.  
    queue: currentHook.queue,
  64.  
     
  65.  
    next: null, // 注意next指针是null
  66.  
     
  67.  
    };
  68.  
     
  69.  
    if (workInProgressHook === null) {
  70.  
     
  71.  
    currentlyRenderingFiber.memoizedState = workInProgressHook = newHook;
  72.  
     
  73.  
    } else {
  74.  
     
  75.  
    workInProgressHook = workInProgressHook.next = newHook;
  76.  
     
  77.  
    }
  78.  
     
  79.  
    }
  80.  
     
  81.  
    return workInProgressHook;
  82.  
     
  83.  
    }
学新通

updateWorkInProgressHook函数逻辑简单: 目的是为了让currentHookworkInProgressHook两个指针同时向后移动.

  1. 由于renderWithHooks函数设置了workInProgress.memoizedState=null, 所以workInProgressHook初始值必然为null, 只能从currentHook克隆.
  2. 而从currentHook克隆而来的newHook.next=null, 进而导致workInProgressHook链表需要完全重建.

所以function执行完成之后, 有关Hook的内存结构如下:

学新通

可以看到:

  1. 以双缓冲技术为基础, 将current.memoizedState按照顺序克隆到了workInProgress.memoizedState中.
  2. Hook经过了一次克隆, 内部的属性(hook.memoizedState等)都没有变动, 所以其状态并不会丢失.

学新通

总结

本节首先引入了官方文档上对于Hook的解释, 了解Hook的由来, 以及Hook相较于class的优势. 然后从fiber视角分析了fiberhook的内在关系, 通过renderWithHooks函数, 把Hook链表挂载到了fiber.memoizedState之上. 利用fiber树内部的双缓冲技术, 实现了HookcurrentworkInProgress转移, 进而实现了Hook状态的持久化.

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfiegig
系列文章
更多 icon
同类精品
更多 icon
继续加载