React hooks 之useState详解

70 阅读4分钟

前提

React函数组件里有很多hooks,他们在不同的阶段,dispatch的是不同的

renderWithHooks函数里,会根据不同的阶段,分别赋值

这是在mount阶段和update的赋值

ReactCurrentDispatcher.current =
      current === null || current.memoizedState === null
        ? HooksDispatcherOnMount
        : HooksDispatcherOnUpdate;
        

render阶段

  if (didScheduleRenderPhaseUpdateDuringThisPass) {
  ReactCurrentDispatcher.current = __DEV__
        ? HooksDispatcherOnRerenderInDEV
        : HooksDispatcherOnRerender;
  }

执行完函数后变成context

 ReactCurrentDispatcher.current = ContextOnlyDispatcher;

useState的实现入口,其中resolveDispatcher返回的就是ReactCurrentDispatcher.current

export function useState<S>(
  initialState: (() => S) | S, //初始值 单值或者函数
): [S, Dispatch<BasicStateAction<S>>] {
  const dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}

当react初始节点时,current是为空的,useState调用的是HooksDispatcherOnMount HooksDispatcherOnMount.useState实际就是mountState

mount阶段

mountState

mountState函数接收一个初始状态,可能是函数或值。函数内部调用了mountWorkInProgressHook获取当前正在处理的Hook节点。

然后判断initialState是否是函数类型,如果是就执行它获取初始值。接着将memoizedState和baseState都初始化为这个值。创建一个更新队列queue,设置基本的reducer为basicStateReducer,最后绑定dispatchSetState方法到dispatch函数上,并返回状态和dispatch

组件挂载 → mountState → 初始化状态 → 返回[state, dispatch]
                  ↓
      创建更新队列并与当前Fiber绑定
graph TD
    A[开始] --> B{初始值是函数?}
    B -->|是| C[执行函数获取状态]
    B -->|否| D[直接使用初始值]
    C & D --> E[设置memoized/baseState]
    E --> F[创建更新队列]
    F --> G[绑定dispatch函数]
    G --> H[返回状态和dispatch]
function mountState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  // 1. 获取当前处理中的Hook节点
  const hook = mountWorkInProgressHook();
  
  // 2. 初始化状态处理(支持函数式初始值)
  if (typeof initialState === 'function') {
    initialState = initialState(); // 执行函数获取初始值
  }
  
  // 3. 设置Hook状态基准
  hook.memoizedState = hook.baseState = initialState;
  
  // 4. 创建更新队列
  const queue: UpdateQueue<S, BasicStateAction<S>> = {
    pending: null,       // 待处理更新链表
    lanes: NoLanes,      // 更新优先级车道
    dispatch: null,      // 状态更新分发器
    lastRenderedReducer: basicStateReducer, // 基础状态处理器
    lastRenderedState: initialState,        // 最后渲染状态
  };
  hook.queue = queue;
  
  // 5. 绑定dispatch函数
  const dispatch = (queue.dispatch = dispatchSetState.bind(
    null,
    currentlyRenderingFiber,
    queue,
  ));
  
  return [hook.memoizedState, dispatch];
}

mountWorkInProgressHook

mountWorkInProgressHook函数内部创建了一个新的Hook对象,包含memoizedStatebaseStatebaseQueuequeuenext等属性。接下来,函数检查当前是否有正在处理的Hook(workInProgressHook是否为null)。如果是第一次调用,就将当前渲染的Fiber节点的memoizedState指向这个新创建的Hook,并将workInProgressHook也指向它。否则,将新Hook添加到链表末尾,并更新workInProgressHook的指向

  • 单向链表结构 :通过 next 指针形成执行顺序链,严格保持Hooks调用顺序
  • 双指针管理 :
    • currentlyRenderingFiber.memoizedState 指向链表头部
    • workInProgressHook 始终指向链表尾部
  • 状态隔离 :每个Hook维护独立的状态和更新队列,避免相互干扰 该函数在Hooks系统中的定位:
组件挂载 → mountWorkInProgressHook → 创建Hook链表 → 维护调用顺序
                      ↓
  实现Hooks规则的核心基础(保证每次渲染的Hook顺序一致性)
graph TD
    A[创建Hook对象] --> B{是否是第一个Hook?}
    B -->|是| C[绑定到Fiber.memoizedState]
    B -->|否| D[追加到链表末尾]
    C & D --> E[返回当前Hook指针]
function mountWorkInProgressHook(): Hook {
  // 创建新的Hook对象
  const hook: Hook = {
    memoizedState: null,   // 当前状态
    baseState: null,       // 基准状态
    baseQueue: null,       // 未处理的更新队列
    queue: null,           // 更新队列指针
    next: null,            // 链表指针
  };

  // 链表连接逻辑
  if (workInProgressHook === null) {
    // 第一个Hook:连接Fiber节点
    currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
  } else {
    // 后续Hook:追加到链表末尾
    workInProgressHook = workInProgressHook.next = hook;
  }
  return workInProgressHook;
}

dispatchSetState

函数接收三个参数:fiberqueueaction

fiber代表当前的Fiber节点,queue是更新队列,action是新的状态值或更新函数。

然后,函数通过requestUpdateLane获取当前更新的优先级车道(lane)。创建一个新的更新对象update,包含优先级、action、预计算状态标记和下一个更新的指针

判断当前是否处于渲染阶段更新。如果是,将更新加入渲染阶段的队列。否则,进入常规更新处理流程

setState调用 → dispatchSetState → 创建更新对象 → 加入更新队列
                      ↓
      React并发模式状态更新的核心调度入口
graph TD
    A[开始] --> B{是否渲染阶段更新?}
    B -->|是| C[加入渲染阶段队列]
    B -->|否| D{是否空队列?}
    D -->|是| E[预计算状态]
    E --> F{状态是否变化?}
    F -->|否| G[跳过渲染]
    F -->|是| H[加入常规队列]
    D -->|否| H
    C & G & H --> I[结束]
function dispatchSetState<S, A>(
  fiber: Fiber,
  queue: UpdateQueue<S, A>,
  action: A,
) {
  // 开发环境回调参数检查
  if (__DEV__) {
    if (typeof arguments[3] === 'function') {
      console.error("状态更新不支持回调参数...");
    }
  }

  // 1. 获取更新优先级车道
  const lane = requestUpdateLane(fiber);

  // 2. 创建更新对象
  const update: Update<S, A> = {
    lane,
    action,
    hasEagerState: false,  // 预计算标记
    eagerState: null,      // 预计算结果
    next: null,            // 链表指针
  };

  // 3. 判断渲染阶段更新
  if (isRenderPhaseUpdate(fiber)) {
    enqueueRenderPhaseUpdate(queue, update);
  } else {
    // 4. 非渲染阶段更新处理
    const alternate = fiber.alternate;
    if (fiber.lanes === NoLanes && alternate?.lanes === NoLanes) {
      // 5. 快速路径:预计算状态
      const lastRenderedReducer = queue.lastRenderedReducer;
      if (lastRenderedReducer !== null) {
        try {
          const currentState = queue.lastRenderedState;
          const eagerState = lastRenderedReducer(currentState, action);
          
          // 6. 状态比较优化
          update.hasEagerState = true;
          update.eagerState = eagerState;
          if (is(eagerState, currentState)) {
            // 状态相同则跳过渲染
            enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update);
            return;
          }
        } catch (error) {
          // 静默处理错误,留到渲染阶段抛出
        }
      }
    }

    // 7. 常规更新路径
    const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
    if (root !== null) {
      scheduleUpdateOnFiber(root, fiber, lane, requestEventTime());
      entangleTransitionUpdate(root, queue, lane);
    }
  }

  // 8. 开发工具标记
  markUpdateInDevTools(fiber, lane, action);
}

后续通过scheduleUpdateOnFiber对fiber进行调度更新