【前端面试】看react源码,解读useState

点击:react git 链接

截止2024.8.22最新版本如下在这里插入图片描述

React hooks

源码好深,hook封装位于packages/react-reconciler/src/ReactFiberHooks.js

hook的数据类型:

export type Hook = {
   
  memoizedState: any,
  baseState: any,
  baseQueue: Update<any, any> | null,
  queue: any,
  next: Hook | null,
};

HooksDispatcher

在 React 18 及更新的版本中,HooksDispatcher 是一个内部使用的调度器,它负责协调 React 组件的挂载(mount)和更新(update)过程中的 Hooks 调用。HooksDispatcherOnMountHooksDispatcherOnUpdateHooksDispatcher 在不同渲染阶段的实例,它们分别用于处理组件首次挂载和随后的更新。
HooksDispatcherOnMountHooksDispatcherOnUpdate 是 React 为了更好地控制组件渲染过程中 Hooks 的行为而引入的内部调度器实例。它们确保了 Hooks 在组件的不同生命周期阶段能够正确地执行。

HooksDispatcherOnMount

  • HooksDispatcherOnMount 是在组件首次挂载时使用的调度器实例。
  • 在这个阶段,React 会调用组件内的所有 Hooks,例如 useState, useEffect, useContext 等,并且是按照它们在代码中出现的顺序进行调用。
  • 由于是首次渲染,useState 会为每个 state 创建初始状态,useEffect 会执行所有 effect 的逻辑(但不会清除,因为没有之前的 effect)。
    在这里插入图片描述

HooksDispatcherOnUpdate

  • HooksDispatcherOnUpdate 是在组件更新时使用的调度器实例。
  • 在更新阶段,React 同样会调用组件内的所有 Hooks,但这次调用是有条件的。React 会根据调度器来判断 Hooks 是否需要被调用。
  • 例如,useState 会返回当前的状态值,useEffect 会根据 effect 的依赖项来决定是否执行 effect。
  • 更新阶段的 Hooks 调用通常涉及到比较前后的状态或 props,以确定是否需要执行某些操作或副作用。
    在这里插入图片描述

为什么需要不同的调度器实例?

  • 性能优化:通过在不同的渲染阶段使用不同的调度器实例,React 可以更精确地控制 Hooks 的行为,从而优化性能。
  • 避免副作用的重复执行:在更新阶段,React 需要区分哪些副作用需要重新执行,哪些可以保持不变。
  • 保持渲染的一致性:确保组件的渲染行为在不同的渲染阶段保持一致性。

useState解析

useState解析-mountState

mountState就是useState的实现,间接调用了mountStateImpl、mountWorkInProgressHook。下面展开详细解读

mountWorkInProgressHook

  • 拿到当前FiberNode的workInProgressHook变量(可以将其理解为hooks链表的指针),将其指向当前最新hook,并返回引用。
  • currentlyRenderingFiber.memoizedState指向hooks链表的头指针。

function mountWorkInProgressHook(): Hook {
   
  const hook: Hook = {
   
    memoizedState: null,

    baseState: null,
    baseQueue: null,
    queue: null,

    next: null,
  };

  if (workInProgressHook === null) {
   
    // This is the first hook in the list
    currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
  } else {
   
    // Append to the end of the list
    workInProgressHook = workInProgressHook.next = hook;
  }
  return workInProgressHook;
}

通过调用,先将hook挂在到filber node的hooks链表上
计算传入的初始值,并赋值给hook.memoizedState
给hook.queue 赋初值。创建一个新的链表作为更新队列,用来存放更新(setXxx())

function mountStateImpl<S>(initialState: (() => S) | S): Hook {
   
  const hook = mountWorkInProgressHook();
  if (typeof initialState === 'function') {
   
    const initialStateInitializer = initialState;
    // $FlowFixMe[incompatible-use]: Flow doesn't like mixed types
    initialState = initialStateInitializer();
    if (shouldDoubleInvokeUserFnsInHooksDEV) {
      setIsStrictModeForDevtools(true);
      // $FlowFixMe[incompatible-use]: Flow doesn't like mixed types
      initialStateInitializer();
      setIsStrictModeForDevtools(false);
    }
  }
  hook.memoizedState = hook.baseState = initialState;
  const queue: UpdateQueue<S, BasicStateAction<S>> = {
   
    pending: null,
    lanes: NoLanes,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: (initialState: any),
  };
  hook.queue = queue;
  return hook;
}

创建一个 dispatch 示例方法(即 useState 返回的数组的第二个参数:setXxx()),
该方法的作用是用来修改 state,并将此更新添加到更新队列中,通过 .bind 把当前 fiber node、更新队列、 dispatch 方法关联起来:


function mountState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
   
  const hook = mountStateImpl(initialState);
  const queue = hook.queue;
  const dispatch: Dispatch<BasicStateAction<S>> = (dispatchSetState.bind(
    null,
    currentlyRenderingFiber,
    queue,
  ): any);
  queue.dispatch = dispatch;
  return [hook.memoizedState, dispatch];
}

综上,useState 在 Mount (组件初始化)阶段:

  1. 获取当前 Hook 节点,同时将当前 Hook 添加到 Hook 链表中
  2. 初始化 Hook 的状态,即读取初始 state 值
  3. 创建一个新的链表作为更新队列,用来存放更新操作setXxx(),
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值