React19源码系列之 Hooks (useState、useReducer、useOptimistic)

此次记录钩子函数useState、useReducer、useOptimistic的使用和源码分析,这些钩子函数都会触发组件的重新渲染。

useState:useState -- React 中文文档

useReducer:useReducer -- React 中文文档

useOptimistic:useOptimistic -- React 中文文档

fiber、updateQueue、hook、queue、update关联结构图

useReducer

React 中 useReducer Hook 的入口函数,主要负责 分发调用参数传递

javascript 复制代码
function useReducer<S, I, A>(
  reducer: (S, A) => S,
  initialArg: I,
  init?: I => S,
): [S, Dispatch<A>] {
  const dispatcher = resolveDispatcher();
  return dispatcher.useReducer(reducer, initialArg, init);
}

resolveDispatcher 函数的主要作用是解析并返回当前 React 环境中的调度器(dispatcher)。调度器在 React 中扮演着至关重要的角色,它负责管理组件的更新、渲染调度等任务,确保 React 应用能够高效、有序地运行。

javascript 复制代码
function resolveDispatcher() {
  // 从 ReactSharedInternals 对象中获取名为 H 的属性,并将其赋值给变量 dispatcher。ReactSharedInternals 是 React 内部使用的一个共享对象,它包含了一些 React 运行时的关键信息和工具。H 属性在这里代表着当前 React 环境下的调度器实例。
  const dispatcher = ReactSharedInternals.H;
  // 将获取到的调度器实例 dispatcher 返回给调用者。这样,调用 resolveDispatcher 函数的代码就可以使用这个调度器来执行各种调度相关的操作,比如调用调度器的 useState 方法来处理状态管理。
  return dispatcher;
}

一、mountReducer

React 中 useReducer Hook 在 挂载阶段(mount) 的核心实现,主要负责 初始化状态创建 dispatch 函数

javascript 复制代码
function mountReducer<S, I, A>(
  reducer: (S, A) => S,
  initialArg: I,
  init?: I => S,
): [S, Dispatch<A>] {
  // 初始化 Hook 对象
  const hook = mountWorkInProgressHook();
  
  let initialState;

  // init如果存在,使用 init(initialArg) 的执行结果作为初始值,否则使用 initialArg。
  if (init !== undefined) {
    initialState = init(initialArg);
  } else {
    initialState = ((initialArg: any): S);
  }
  
  hook.memoizedState = hook.baseState = initialState;

  // 创建更新队列
  const queue: UpdateQueue<S, A> = {
    pending: null, //  待处理的更新(循环链表)
    lanes: NoLanes, // 当前队列的优先级
    dispatch: null,
    lastRenderedReducer: reducer, // 最后一次渲染时使用的 reducer
    lastRenderedState: (initialState: any), // 最后一次渲染时的状态
  };
  
  hook.queue = queue;

  // 创建并绑定 dispatch 函数
  const dispatch: Dispatch<A> = (queue.dispatch = (dispatchReducerAction.bind(
    null,
    currentlyRenderingFiber,
    queue,
  ): any));
  
  return [hook.memoizedState, dispatch];
}

dispatchReducerAction

React 中 useReduceruseState状态更新调度器 ,负责 创建更新对象入队操作触发渲染

javascript 复制代码
function dispatchReducerAction<S, A>(
  fiber: Fiber,
  queue: UpdateQueue<S, A>,
  action: A,
): void {

  const lane = requestUpdateLane(fiber);

  // 1. 创建 Update 对象
  const update: Update<S, A> = {
    lane,
    revertLane: NoLane,
    action,
    hasEagerState: false,
    eagerState: null,
    next: (null: any),
  };

  // 渲染阶段更新处理
  if (isRenderPhaseUpdate(fiber)) {
    // 场景:在 render 或 useMemo 等同步渲染过程中调用 dispatch。
    // 特点:直接将更新加入当前渲染队列,不触发额外调度。
    enqueueRenderPhaseUpdate(queue, update);
    
  } else {
     // 并发更新处理

    // 入队操作:将更新添加到全局队列,等待调度。
    const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
    
    if (root !== null) {
       // 启动更新计时器
      // startUpdateTimerByLane(lane);
      // / 调度更新
      scheduleUpdateOnFiber(root, fiber, lane);
      // 处理过渡更新
      // entangleTransitionUpdate(root, queue, lane);
    }
  }

  // markUpdateInDevTools(fiber, lane, action);
}

enqueueRenderPhaseUpdate

enqueueRenderPhaseUpdate 函数的主要作用是处理渲染阶段的更新操作。在 React 中,当更新发生在渲染阶段时,会调用这个函数将更新操作暂存到一个更新队列中,以便在当前渲染阶段结束后,重新启动渲染并应用这些暂存的更新到正在处理的 Hook 上。

函数参数含义:

  • queue:类型为 UpdateQueue<S, A>,表示 Hook 的更新队列,用于存储与状态更新相关的信息,包括之前的状态、更新的动作等。
  • update:类型为 Update<S, A>,代表具体的更新操作对象,包含了更新的优先级车道(lane)、回滚车道(revertLane)、更新动作(action)、是否有提前计算的状态(hasEagerState)以及提前计算的状态值(eagerState)等信息。
javascript 复制代码
function enqueueRenderPhaseUpdate<S, A>(
  queue: UpdateQueue<S, A>,
  update: Update<S, A>,
): void {
  
  // 用于记录在当前渲染阶段已经调度了渲染阶段的更新操作
  didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate =
    true;

  // 循环链表
  const pending = queue.pending;
  if (pending === null) {
    // This is the first update. Create a circular list.
    update.next = update;
  } else {
    update.next = pending.next;
    pending.next = update;
  }

 // update 成为队列中当前待处理的更新。
  queue.pending = update;
}

enqueueConcurrentHookUpdate

enqueueConcurrentHookUpdate 函数主要用于将一个并发的 Hook 更新操作加入到更新队列中,并且返回更新所关联的 FiberRoot

函数参数含义

  • fiber:当前执行更新操作的 Fiber 节点。
  • queueHook 的更新队列,类型为 HookQueue<S, A>,其中 S 表示状态的类型,A 表示动作(action)的类型。更新队列用于存储一系列的更新操作。
  • update:具体的 Hook 更新操作,类型为 HookUpdate<S, A>,包含了更新所需的信息,如更新的动作、优先级等。
  • lane:更新的优先级车道(Lane)。在 React 的调度系统中,不同的更新会被分配到不同的车道,车道决定了更新的优先级,高优先级的车道会优先被处理。
javascript 复制代码
function enqueueConcurrentHookUpdate<S, A>(
  fiber: Fiber,
  queue: HookQueue<S, A>,
  update: HookUpdate<S, A>,
  lane: Lane,
): FiberRoot | null {

  // 类型转换
  const concurrentQueue: ConcurrentQueue = (queue: any);
  const concurrentUpdate: ConcurrentUpdate = (update: any);

  // 加入更新队列
  enqueueUpdate(
    fiber, 
    concurrentQueue, // 更新队列
    concurrentUpdate, // 具体的更新操作
    lane
  );

  // 返回关联的 FiberRoot
  return getRootForUpdatedFiber(fiber);
}

工具函数之 enqueueUpdate

React 更新队列管理的核心函数,主要处理 更新入队优先级标记

javascript 复制代码
function enqueueUpdate<State>(
  fiber: Fiber,
  update: Update<State>,
  lane: Lane,
): FiberRoot | null {
  
  const updateQueue = fiber.updateQueue;
  if (updateQueue === null) {
    // Only occurs if the fiber has been unmounted.
    return null;
  }

  const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;

  if (isUnsafeClassRenderPhaseUpdate(fiber)) {
    // This is an unsafe render phase update. Add directly to the update
    // queue so we can process it immediately during the current render.
    const pending = sharedQueue.pending;
    
    if (pending === null) {
      // 创建循环链表
      // This is the first update. Create a circular list.
      update.next = update;
    } else {
      // 插入到链表末尾
      update.next = pending.next;
      pending.next = update;
    }
    sharedQueue.pending = update;

    // unsafe_markUpdateLaneFromFiberToRoot 将 lane 标记到根节点,触发同步渲染。
    return unsafe_markUpdateLaneFromFiberToRoot(fiber, lane);
  } else {

    // 1. 处理交错更新(interleaved)
  // 2. 将更新添加到 pending 队列
  // 3. 标记优先级到根节点
  // 4. 返回根节点,触发调度
    return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane);
  }
}
javascript 复制代码
const concurrentQueues: Array<any> = [];
let concurrentQueuesIndex = 0;

let concurrentlyUpdatedLanes: Lanes = NoLanes;

工具函数之 isUnsafeClassRenderPhaseUpdate

React 用于 检测不安全的类组件渲染阶段更新 的辅助函数,主要用于 开发模式下的警告提示

javascript 复制代码
function isUnsafeClassRenderPhaseUpdate(fiber: Fiber): boolean {
  return (executionContext & RenderContext) !== NoContext;
}

工具函数之 unsafe_markUpdateLaneFromFiberToRoot

React 中用于 标记更新优先级 的核心函数,主要在 同步更新 场景下使用。

javascript 复制代码
function unsafe_markUpdateLaneFromFiberToRoot(
  sourceFiber: Fiber,
  lane: Lane,
): FiberRoot | null {
  const root = getRootForUpdatedFiber(sourceFiber);
  markUpdateLaneFromFiberToRoot(sourceFiber, null, lane);
  return root;
}

工具函数之 markUpdateLaneFromFiberToRoot

React 中用于标记更新优先级并在 Fiber 树中传播的核心函数,主要作用是将更新的优先级从当前 Fiber 节点向上传播到根节点,确保整个更新流程的优先级管理。

javascript 复制代码
function markUpdateLaneFromFiberToRoot(
  sourceFiber: Fiber,
  update: ConcurrentUpdate | null,
  lane: Lane,
): void {
  // Update the source fiber's lanes
   // 1. 标记当前 Fiber 的 lanes
  sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);

   // 2. 标记双缓存 Fiber 的 lanes
  let alternate = sourceFiber.alternate;
  if (alternate !== null) {
    alternate.lanes = mergeLanes(alternate.lanes, lane);
  }
  // Walk the parent path to the root and update the child lanes.
  let isHidden = false;

  // 3. 向上遍历父节点,标记 childLanes
  let parent = sourceFiber.return;
  let node = sourceFiber;
  
  while (parent !== null) {
    parent.childLanes = mergeLanes(parent.childLanes, lane);
    alternate = parent.alternate;
    
    if (alternate !== null) {
      alternate.childLanes = mergeLanes(alternate.childLanes, lane);
    }

    // 检测离屏组件
    if (parent.tag === OffscreenComponent) {
      const offscreenInstance: OffscreenInstance | null = parent.stateNode;
      if (
        offscreenInstance !== null &&
        !(offscreenInstance._visibility & OffscreenVisible)
      ) {
        // 若父节点是离屏组件且不可见,标记isHidden为true。
        isHidden = true;
      }
    }

    node = parent;
    parent = parent.return;
  }

  // 处理隐藏更新
  if (isHidden && update !== null && node.tag === HostRoot) {
    const root: FiberRoot = node.stateNode;
    // 将更新标记为隐藏状态
    markHiddenUpdate(root, update, lane);
  }
}

工具函数之 markHiddenUpdate

React 中处理 隐藏更新(Hidden Updates) 的核心函数,主要用于 暂存离屏组件的更新,避免不必要的渲染。

javascript 复制代码
 function markHiddenUpdate(
  root: FiberRoot,
  update: ConcurrentUpdate,
  lane: Lane,
) {
   // 按优先级索引存储更新
  const index = laneToIndex(lane);
  const hiddenUpdates = root.hiddenUpdates;
   
  const hiddenUpdatesForLane = hiddenUpdates[index];
   
  if (hiddenUpdatesForLane === null) {
    hiddenUpdates[index] = [update];
  } else {
    hiddenUpdatesForLane.push(update);
  }
   
   // update.lane 同时包含原始优先级和离屏标记。
  update.lane = lane | OffscreenLane;
}
javascript 复制代码
root.hiddenUpdates = [
  [/* 优先级0的更新数组 */],
  [/* 优先级1的更新数组 */],
  // ...
];
javascript 复制代码
function laneToIndex(lane: Lane) {
  return pickArbitraryLaneIndex(lane);
}

工具函数之 enqueueConcurrentClassUpdate

React 中处理 并发模式下类组件更新入队 的核心逻辑,主要负责将更新操作添加到并发队列并触发调度。

javascript 复制代码
function enqueueConcurrentClassUpdate<State>(
  fiber: Fiber,
  queue: ClassQueue<State>,
  update: ClassUpdate<State>,
  lane: Lane,
): FiberRoot | null {
  const concurrentQueue: ConcurrentQueue = (queue: any);
  const concurrentUpdate: ConcurrentUpdate = (update: any);
  enqueueUpdate(fiber, concurrentQueue, concurrentUpdate, lane);
  return getRootForUpdatedFiber(fiber);
}

工具函数之 enqueueUpdate

React 并发更新机制的核心实现,主要负责 暂存更新信息标记优先级

javascript 复制代码
function enqueueUpdate(
  fiber: Fiber,
  queue: ConcurrentQueue | null,
  update: ConcurrentUpdate | null,
  lane: Lane,
) {
  // Don't update the `childLanes` on the return path yet. If we already in
  // the middle of rendering, wait until after it has completed.
  concurrentQueues[concurrentQueuesIndex++] = fiber;
  concurrentQueues[concurrentQueuesIndex++] = queue;
  concurrentQueues[concurrentQueuesIndex++] = update;
  concurrentQueues[concurrentQueuesIndex++] = lane;

  concurrentlyUpdatedLanes = mergeLanes(concurrentlyUpdatedLanes, lane);

  fiber.lanes = mergeLanes(fiber.lanes, lane);

  // 立即更新 Fiber 节点的 lanes
  const alternate = fiber.alternate;
  if (alternate !== null) {
    alternate.lanes = mergeLanes(alternate.lanes, lane);
  }
}
javascript 复制代码
concurrentQueues = [
  fiber1, queue1, update1, lane1,
  fiber2, queue2, update2, lane2,
  // ...
];

二、updateReducer

React 中 useReducer Hook 在 更新阶段(update) 的核心实现,主要负责 获取当前 Hook 状态调用更新实现函数

javascript 复制代码
function updateReducer<S, I, A>(
  reducer: (S, A) => S,
  initialArg: I,
  init?: I => S,
): [S, Dispatch<A>] {
  const hook = updateWorkInProgressHook();
  return updateReducerImpl(hook, ((currentHook: any): Hook), reducer);
}

updateReducerImpl

updateReducerImpl 函数是 React 中用于处理 useReducer 钩子状态更新的核心函数。它的主要任务是根据传入的 reducer 函数,处理 Hook 对象中的更新队列,计算出新的状态,并更新 Hook 和相关队列的属性。最终,该函数返回一个包含新状态和状态更新调度函数的数组。

javascript 复制代码
function updateReducerImpl<S, A>(
  hook: Hook,// hook:即前面获取到的当前正在处理的 Hook 对象。
  current: Hook,
  reducer: (S, A) => S,
): [S, Dispatch<A>] {

  // 从 hook 对象中获取更新队列 queue。
  const queue = hook.queue;

  // 将 queue 的 lastRenderedReducer 属性设置为传入的 reducer 函数,记录最后一次使用的 reducer。
  queue.lastRenderedReducer = reducer;

  // 获取 hook 的基础队列 baseQueue 和待处理队列 pendingQueue。
  let baseQueue = hook.baseQueue;
  const pendingQueue = queue.pending;

   // 若 pendingQueue 不为 null,说明有新的更新未处理
  if (pendingQueue !== null) {
    if (baseQueue !== null) {
     // 若 baseQueue 不为 null,则将两个队列合并。
      const baseFirst = baseQueue.next;
      const pendingFirst = pendingQueue.next;
      baseQueue.next = pendingFirst;
      pendingQueue.next = baseFirst;
    }
    current.baseQueue = baseQueue = pendingQueue;
    queue.pending = null;
  }

  const baseState = hook.baseState;
  
  if (baseQueue === null) {
    hook.memoizedState = baseState;
  } else {
    // 基础队列
    // We have a queue to process.
    const first = baseQueue.next;
    
    let newState = baseState;// 新状态
    let newBaseState = null;// 新基础状态
    let newBaseQueueFirst = null;// 新基础队列首节点
    let newBaseQueueLast: Update<S, A> | null = null;// 新基础队列尾节点
    let update = first;
    let didReadFromEntangledAsyncAction = false;
    
    do {
// removeLanes是一个函数,其作用是从update.lane(当前更新的车道)中移除OffscreenLane。
//在 React 的调度系统里,车道代表着更新的优先级,OffscreenLane 可能表示与屏幕外元素相关的更新优先级。
      const updateLane = removeLanes(update.lane, OffscreenLane);

      // 若 updateLane 与 update.lane 不同,就表明原始的更新车道里包含 OffscreenLane,此更新为隐藏更新,反之,则不是隐藏更新。
      const isHiddenUpdate = updateLane !== update.lane;
      
      // 根据是否为隐藏更新来决定是否应跳过当前更新。
      const shouldSkipUpdate = isHiddenUpdate
        // getWorkInProgressRootRenderLanes() 会返回当前正在处理的根节点的渲染车道
        ? !isSubsetOfLanes(getWorkInProgressRootRenderLanes(), updateLane)
        : !isSubsetOfLanes(renderLanes, updateLane);
      // 上述代码,updateLane不是渲染车道的子集,则隐藏更新

      // 处理应跳过的更新
      if (shouldSkipUpdate) {
        
      } else {
        // 处理不应该跳过的更新
      }
      update = update.next;
      
    } while (update !== null && update !== first);
    

    if (newBaseQueueLast === null) {
      newBaseState = newState;
    } else {
      newBaseQueueLast.next = (newBaseQueueFirst: any);
    }

    // 表明新状态和旧状态不同,意味着状态发生了改变
    if (!is(newState, hook.memoizedState)) {
      // 标记工作中的 Fiber 节点接收到更新
      markWorkInProgressReceivedUpdate();

     // 检测到当前更新的车道与纠缠动作车道匹配
      if (didReadFromEntangledAsyncAction) {
        // 用于获取当前纠缠动作的 Promise 对象(entangledActionThenable)。
        const entangledActionThenable = peekEntangledActionThenable();

        // 若 entangledActionThenable 不为 null,说明存在纠缠的异步操作,此时会抛出这个 Promise 对象。在 React 中,抛出 Promise 是一种用于处理异步操作的机制,React 会捕获这个 Promise,暂停当前的渲染过程,等待 Promise 解决后再继续渲染。
        if (entangledActionThenable !== null) {
     
          throw entangledActionThenable;
        }
      }
    }

    hook.memoizedState = newState;
    hook.baseState = newBaseState;
    hook.baseQueue = newBaseQueueLast;

    queue.lastRenderedState = newState;
  }

  if (baseQueue === null) {
    queue.lanes = NoLanes;
  }

  const dispatch: Dispatch<A> = (queue.dispatch: any);
  return [hook.memoizedState, dispatch];
}

isHiddenUpdate是一个布尔值,通过比较updateLane和原始更新车道(update.lane)是否不同来确定当前更新是否为隐藏更新。如果updateLaneupdate.lane不同,说明原始的更新车道里包含OffscreenLane,此更新为隐藏更新。

javascript 复制代码
// 根据是否为隐藏更新来决定是否应跳过当前更新。
const shouldSkipUpdate = isHiddenUpdate
  // getWorkInProgressRootRenderLanes() 会返回当前正在处理的根节点的渲染车道
  ? !isSubsetOfLanes(getWorkInProgressRootRenderLanes(), updateLane)
  : !isSubsetOfLanes(renderLanes, updateLane);
// 上述代码,updateLane不是渲染车道的子集,则隐藏更新
javascript 复制代码
if (shouldSkipUpdate) {
  // 创建新的update对象,并且
  const clone: Update<S, A> = {
    lane: updateLane,// 更新车道
    revertLane: update.revertLane,// 回滚车道
    action: update.action,// 
    hasEagerState: update.hasEagerState,// 是否有提前计算状态
    eagerState: update.eagerState,// 提前计算状态
    next: (null: any),
  };
  
  if (newBaseQueueLast === null) {
    // 将 newBaseQueueFirst 和 newBaseQueueLast 都设置为 clone,同时将 newBaseState 设置为当前的 newState。
    newBaseQueueFirst = newBaseQueueLast = clone;
    newBaseState = newState;
    
  } else {
    // 如果 newBaseQueueLast 不为 null,说明新基础队列已经存在其他更新,此时将克隆的更新 clone 添加到队列的末尾,即将 newBaseQueueLast 的 next 属性指向 clone,然后更新 newBaseQueueLast 为 clone。
    newBaseQueueLast = newBaseQueueLast.next = clone;
  }

  // 调用 mergeLanes 函数,将当前正在渲染的 Fiber 节点的 lanes 属性与当前更新的车道 updateLane 进行合并。这是为了记录当前 Fiber 节点所涉及的所有更新车道,以便后续的调度和处理。
  currentlyRenderingFiber.lanes = mergeLanes(
    currentlyRenderingFiber.lanes,
    updateLane,
  );

  // 标记跳过的更新车道
  markSkippedUpdateLanes(updateLane);
  
}
javascript 复制代码
 if (shouldSkipUpdate) {} else {
  // 处理不应该跳过的更新

   //获取更新队列的回滚车道
  const revertLane = update.revertLane;
  // 通过检查 enableAsyncActions 和 revertLane 的值来判断当前更新是否为乐观更新。
  // enableAsyncActions 是一个布尔值,代表是否启用异步动作
  // revertLane代表回滚
  if (!enableAsyncActions || revertLane === NoLane) {
    // 非乐观更新的处理
    
    // 检查新基础队列是否不为空,即之前是否有跳过的更新。
    if (newBaseQueueLast !== null) {
      
      const clone: Update<S, A> = {
        lane: NoLane,
        revertLane: NoLane,
        action: update.action,
        hasEagerState: update.hasEagerState,
        eagerState: update.eagerState,
        next: (null: any),
      };
      newBaseQueueLast = newBaseQueueLast.next = clone;
    }

    // peekEntangledActionLane() 函数返回的纠缠动作车道
    if (updateLane === peekEntangledActionLane()) {
      // 读取了纠缠的异步动作
      didReadFromEntangledAsyncAction = true;
    }
  } else {
    
    // 乐观更新
    if (isSubsetOfLanes(renderLanes, revertLane)) {
   
      update = update.next;
      if (revertLane === peekEntangledActionLane()) {
        didReadFromEntangledAsyncAction = true;
      }
      continue;
      
    } else {

      const clone: Update<S, A> = {
        lane: NoLane,
        // Reuse the same revertLane so we know when the transition
        // has finished.
        revertLane: update.revertLane,
        action: update.action,
        hasEagerState: update.hasEagerState,
        eagerState: update.eagerState,
        next: (null: any),
      };
      
      if (newBaseQueueLast === null) {
        newBaseQueueFirst = newBaseQueueLast = clone;
        newBaseState = newState;
      } else {
        newBaseQueueLast = newBaseQueueLast.next = clone;
      }
      currentlyRenderingFiber.lanes = mergeLanes(
        currentlyRenderingFiber.lanes,
        revertLane,
      );
      markSkippedUpdateLanes(revertLane);
    }
  }

  // Process this update.
  const action = update.action;

  // 计算新状态
  if (update.hasEagerState) {
    newState = ((update.eagerState: any): S);
  } else {
    newState = reducer(newState, action);
  }
}

流程图

useState

useState 函数是 React 中用于在函数组件中添加状态的重要 Hook。它的主要作用是为函数组件引入状态管理能力,允许组件在渲染过程中拥有和更新状态值。该函数接收一个初始状态值(可以是一个值或者一个返回值的函数),并返回一个包含当前状态值和用于更新状态的调度函数的数组。

javascript 复制代码
function useState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  // 调用 resolveDispatcher 函数来获取当前的调度器实例。在 React 的架构中,调度器负责管理组件的更新、渲染等操作。resolveDispatcher 函数会根据当前的上下文环境(例如,是否处于渲染阶段、是否在服务器端渲染等)来确定并返回正确的调度器实例。
  const dispatcher = resolveDispatcher();
  
  // 调用调度器的 useState 方法
  return dispatcher.useState(initialState);
}

initialState 可以是两种形式:

  • 直接传入一个具体的状态值,例如 useState(0),此时初始状态就是 0
  • 传入一个函数,例如 useState(() => []),在这种情况下,只有在组件首次渲染时才会调用这个函数,函数的返回值将作为初始状态。这样做的好处是可以避免在每次渲染时都重新计算初始状态(因为函数组件在每次更新时都会重新执行)。

useState的用法

1、简单基础用法:

javascript 复制代码
import React, { useState } from 'react';

function Counter() {
  // 声明一个名为 count 的状态变量,初始值为 0
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <button onClick={() => setCount(prev => prev - 1)}>Decrement</button>
    </div>
  );
}

2、当初始值需要通过复杂计算获得时,可以传入一个函数:

javascript 复制代码
function ExpensiveComponent() {
  // 只会在组件初始化时执行一次
  const [data, setData] = useState(() => {
    return expensiveComputation(); // 复杂计算
  });
  
  // ...
}

3、对于对象或数组状态,需要更新部分值:

javascript 复制代码
function Form() {
  const [formData, setFormData] = useState({
    name: '',
    email: ''
  });

  const handleChange = (e) => {
    // 合并更新部分字段
    setFormData(prev => ({
      ...prev,
      [e.target.name]: e.target.value
    }));
  };
  
  // ...
}

一、mountState

mountState 函数是 React 中用于在组件挂载阶段初始化 useState 钩子的核心函数。useState 是 React 函数式组件中用于管理状态的重要钩子,mountState 函数的主要任务是创建并初始化状态钩子,为状态管理提供必要的基础设施,包括创建状态钩子实例、设置状态更新队列和返回状态值及更新状态的方法。

函数参数含义:

  • initialState:初始状态值,可以是状态的具体值,也可以是一个返回状态值的函数。如果传入的是函数,React 会在初始化状态时调用该函数并使用其返回值作为初始状态。
javascript 复制代码
function mountState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {

  // 创建并初始化状态钩子实例。它会根据传入的 initialState 初始化状态,并将其存储在钩子的 memoizedState 属性中。hook 对象包含了状态钩子的相关信息,如当前状态值、更新队列等。
  const hook = mountStateImpl(initialState);
  
  // 获取状态更新队列,用于存储待处理的状态更新操作。在 React 中,状态更新并不是立即生效的,而是会被收集到更新队列中,在合适的时机统一处理。
  const queue = hook.queue;

   // 创建状态更新函数
  const dispatch: Dispatch<BasicStateAction<S>> = (dispatchSetState.bind(
    null,
    currentlyRenderingFiber, // 当前渲染到fiber
    queue,// 更新队列
  ): any);

  // 将创建好的 dispatch 函数赋值给 queue 的 dispatch 属性,这样在后续处理更新队列时,可以通过 queue.dispatch 调用该函数。
  queue.dispatch = dispatch;
  
  return [hook.memoizedState, dispatch];
}

mountStateImpl

mountStateImpl 函数的核心功能包括创建新的 Hook 实例、处理初始状态(如果初始状态是函数则执行它)、初始化 Hook 的状态属性,并且为 Hook 配置一个更新队列,最后返回初始化好的 Hook

javascript 复制代码
function mountStateImpl<S>(initialState: (() => S) | S): Hook {
  // 创建hook实例
  const hook = mountWorkInProgressHook();
  
  if (typeof initialState === 'function') {
    const initialStateInitializer = initialState;
    // 计算获得初始值
    initialState = initialStateInitializer();
  }
  // 初始化 Hook 的状态属性
  hook.memoizedState = hook.baseState = initialState;

  // 创建更新队列
  const queue: UpdateQueue<S, BasicStateAction<S>> = {
    pending: null,// 用于存储待处理的更新操作,初始值为 null。
    lanes: NoLanes, // 表示更新的优先级车道,初始值为 NoLanes,即没有指定优先级。
    dispatch: null,// 用于触发状态更新的函数,初始值为 null
    
    // 最后一次渲染时使用的状态更新函数,这里初始化为 basicStateReducer。
    lastRenderedReducer: basicStateReducer,
    
    // 最后一次渲染时的状态值,初始化为 initialState。
    lastRenderedState: (initialState: any),
  };

  // 关联到hook链表
  hook.queue = queue;
  
  return hook;
}

dispatchSetState

dispatchSetState 函数的主要作用是为 setState 操作分配一个更新优先级车道(lane),然后调用内部函数 dispatchSetStateInternal 来处理更新的调度,并且在成功调度更新的情况下,启动一个更新定时器。

函数参数含义:

  • fiber:当前的 Fiber 节点。Fiber 是 React 16 及以后版本中引入的协调算法的基础数据结构,代表了组件树中的一个节点。
  • queue:更新队列,用于存储 setState 操作的相关信息。每个 Fiber 节点都有一个对应的更新队列,用于管理该节点的状态更新。
  • actionsetState 操作传入的动作,通常是一个对象或函数。例如,setState({ count: 1 }) 中的 { count: 1 } 就是一个动作对象;setState(prevState => ({ count: prevState.count + 1 })) 中的 prevState => ({ count: prevState.count + 1 }) 就是一个动作函数。
javascript 复制代码
function dispatchSetState<S, A>(
  fiber: Fiber,
  queue: UpdateQueue<S, A>,
  action: A,
): void {

  // 分配更新优先级车道
  const lane = requestUpdateLane(fiber);

  //调度更新
  const didScheduleUpdate = dispatchSetStateInternal(
    fiber,// 当前的 Fiber 节点
    queue,// queue(更新队列,用于存储 setState 操作的相关信息)
    action,// action(setState 操作传入的动作,通常是一个对象或函数)
    lane,// 更新优先级车道
  );
  // 检查 didScheduleUpdate 的值,如果为 true,说明更新成功调度。
  if (didScheduleUpdate) {
    // startUpdateTimerByLane 函数的作用是根据车道的优先级启动一个更新定时器,用于在合适的时间触发更新操作,以确保更新能够按照优先级顺序进行处理。
    // 启动更新定时器
    startUpdateTimerByLane(lane);
  }
  // markUpdateInDevTools(fiber, lane, action);
}

dispatchSetStateInternal

dispatchSetStateInternal 函数的主要任务是将状态更新请求(action)封装成一个 Update 对象,并根据不同情况处理这个更新请求,包括在渲染阶段处理更新、尝试提前计算新状态以避免不必要的更新,以及将更新加入队列并调度更新等操作。最后返回一个布尔值,表示是否成功调度了更新。

函数参数含义:

  • fiber:当前执行更新操作的 Fiber 节点。Fiber 是 React 协调算法的基础数据结构,包含了组件的状态、属性等信息。
  • queue:更新队列,存储了与 setState 操作相关的信息,如之前的状态、reducer 函数等。
  • actionsetState 操作传入的动作,通常是一个值或函数,用于更新状态。
  • lane:更新的优先级车道,用于确定更新的执行顺序,不同的车道对应不同的优先级。
javascript 复制代码
function dispatchSetStateInternal<S, A>(
  fiber: Fiber,
  queue: UpdateQueue<S, A>,
  action: A,
  lane: Lane,
): boolean {
  
  // 创建一个更新对象
  const update: Update<S, A> = {
    lane,// 更新的优先级车道,用于确定更新的执行顺序。
    revertLane: NoLane,// 回滚车道,初始值为 NoLane。
    action,// 状态更新的动作,通常是一个值或函数。
    hasEagerState: false,// 标记是否已经提前计算出了新状态,初始值为 false。
    eagerState: null,// 提前计算出的新状态值,初始值为 null。
    next: (null: any),// 用于将多个 Update 对象连接成一个链表,初始值为 null。
  };

  // 调用 isRenderPhaseUpdate 函数判断当前是否处于渲染阶段。
  if (isRenderPhaseUpdate(fiber)) {
    // 调用 enqueueRenderPhaseUpdate 函数将更新对象添加到渲染阶段的更新队列中。
    enqueueRenderPhaseUpdate(queue, update);
  } else {
    const alternate = fiber.alternate;
    if (
      fiber.lanes === NoLanes &&
      (alternate === null || alternate.lanes === NoLanes)
    ) {
     
      const lastRenderedReducer = queue.lastRenderedReducer;

      // 需要提前计算新状态
      if (lastRenderedReducer !== null) {
        let prevDispatcher = null;

        try {
          const currentState: S = (queue.lastRenderedState: any);
          const eagerState = lastRenderedReducer(currentState, action);
       
          update.hasEagerState = true;
          update.eagerState = eagerState;

          // 如果新状态 eagerState 和当前状态 currentState 相等
          // 表示不需要更新
          if (is(eagerState, currentState)) {
            enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update);
            return false;
          }
        } 
      }
    }

    // 将更新对象添加到并发更新队列中,并获取相关的 FiberRoot。
    const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
    if (root !== null) {
      // 调度更新
      scheduleUpdateOnFiber(root, fiber, lane);

      // 处理过渡更新
      entangleTransitionUpdate(root, queue, lane);
      return true;
    }
  }
  return false;
}

工具函数之 isRenderPhaseUpdate

isRenderPhaseUpdate 函数的主要作用是判断当前的 Fiber 节点是否处于渲染阶段。通过检查 Fiber 节点及其备用 Fiber 节点(alternate)是否与当前正在渲染的 Fiber 节点一致,来确定该 Fiber 节点的更新是否发生在渲染阶段。

javascript 复制代码
function isRenderPhaseUpdate(fiber: Fiber): boolean {
  const alternate = fiber.alternate;
  return (
    fiber === currentlyRenderingFiber ||
    (alternate !== null && alternate === currentlyRenderingFiber)
  );
}

enqueueConcurrentHookUpdateAndEagerlyBailout

React 中处理 并发 Hook 更新 的特殊优化路径,主要用于 跳过优先级调度立即处理更新

javascript 复制代码
function enqueueConcurrentHookUpdateAndEagerlyBailout<S, A>(
  fiber: Fiber,
  queue: HookQueue<S, A>,
  update: HookUpdate<S, A>,
): void {

  const lane = NoLane;
  const concurrentQueue: ConcurrentQueue = (queue: any);
  const concurrentUpdate: ConcurrentUpdate = (update: any);

  enqueueUpdate(fiber, concurrentQueue, concurrentUpdate, lane);

  const isConcurrentlyRendering = getWorkInProgressRoot() !== null;
  if (!isConcurrentlyRendering) {
    finishQueueingConcurrentUpdates();
  }
}
javascript 复制代码
function enqueueUpdate(
  fiber: Fiber,
  queue: ConcurrentQueue | null,
  update: ConcurrentUpdate | null,
  lane: Lane,
) {
  // Don't update the `childLanes` on the return path yet. If we already in
  // the middle of rendering, wait until after it has completed.
  concurrentQueues[concurrentQueuesIndex++] = fiber;
  concurrentQueues[concurrentQueuesIndex++] = queue;
  concurrentQueues[concurrentQueuesIndex++] = update;
  concurrentQueues[concurrentQueuesIndex++] = lane;

  concurrentlyUpdatedLanes = mergeLanes(concurrentlyUpdatedLanes, lane);

  // The fiber's `lane` field is used in some places to check if any work is
  // scheduled, to perform an eager bailout, so we need to update it immediately.
  // TODO: We should probably move this to the "shared" queue instead.
  fiber.lanes = mergeLanes(fiber.lanes, lane);
  const alternate = fiber.alternate;
  if (alternate !== null) {
    alternate.lanes = mergeLanes(alternate.lanes, lane);
  }
}

工具函数之 finishQueueingConcurrentUpdates

React 并发更新机制中 完成更新队列处理 的核心函数,主要负责 将暂存的更新合并到队列标记优先级

javascript 复制代码
function finishQueueingConcurrentUpdates(): void {

  const endIndex = concurrentQueuesIndex;
    // 重置全局变量
  concurrentQueuesIndex = 0;
  concurrentlyUpdatedLanes = NoLanes;

  let i = 0;
  while (i < endIndex) {
    const fiber: Fiber = concurrentQueues[i];
    // 将元素置为 null,释放内存。
    concurrentQueues[i++] = null;
    const queue: ConcurrentQueue = concurrentQueues[i];
    concurrentQueues[i++] = null;
    const update: ConcurrentUpdate = concurrentQueues[i];
    concurrentQueues[i++] = null;
    const lane: Lane = concurrentQueues[i];
    concurrentQueues[i++] = null;

    // 构建循环链表
    if (queue !== null && update !== null) {
      const pending = queue.pending;
      if (pending === null) {
        // This is the first update. Create a circular list.
        update.next = update;
      } else {
        update.next = pending.next;
        pending.next = update;
      }
      queue.pending = update;
    }

    if (lane !== NoLane) {
      // 标记优先级
      markUpdateLaneFromFiberToRoot(fiber, update, lane);
    }
  }
}

二、updateState

updateState 函数是 React 内部用于更新状态的函数,它本质上是对 updateReducer 函数的封装。该函数借助 basicStateReducer 这个基础的状态归约函数,以一种更简洁的方式实现状态更新逻辑,其功能与 useState 类似,旨在为函数组件提供状态管理能力。

javascript 复制代码
function updateState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  return updateReducer(basicStateReducer, initialState);
}

工具函数之 basicStateReducer

basicStateReducer 是 React 中用于处理状态更新的基础归约函数,通常在 useState 钩子的内部实现里被使用。它接收当前状态 state 和一个动作 action 作为参数,根据 action 的类型来决定如何更新状态,并返回更新后的状态。

javascript 复制代码
function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
  return typeof action === 'function' ? action(state) : action;
}

updateReducer

React 中 useReducer Hook 的 更新阶段 实现,主要用于 处理组件重新渲染时的 reducer 状态更新逻辑

javascript 复制代码
function updateReducer<S, I, A>(
  reducer: (S, A) => S,
  initialArg: I,// 初始值
  init?: I => S,
): [S, Dispatch<A>] {
  // 获取正在工作的 Hook
  const hook = updateWorkInProgressHook();

  // 调用 updateReducerImpl 函数处理状态更新
  return updateReducerImpl(hook, ((currentHook: any): Hook), reducer);
}

流程图

useOptimistic

useOptimistic 钩子函数用于实现 乐观更新(Optimistic Updates),即在发起请求前先假设操作成功并立即更新 UI,待服务器确认后再处理实际结果。

javascript 复制代码
function useOptimistic<S, A>(
  passthrough: S,
  reducer: ?(S, A) => S,
): [S, (A) => void] {
  const dispatcher = resolveDispatcher();
  return dispatcher.useOptimistic(passthrough, reducer);
}

一、mountOptimistic

React 中 useOptimistic Hook 的 挂载阶段 实现,主要用于 初始化乐观状态和更新函数

javascript 复制代码
function mountOptimistic<S, A>(
  passthrough: S,
  reducer: ?(S, A) => S,
): [S, (A) => void] {
  // 初始化 Hook 状态
  const hook = mountWorkInProgressHook();
  hook.memoizedState = hook.baseState = passthrough;
  
  const queue: UpdateQueue<S, A> = {
    pending: null,
    lanes: NoLanes,
    dispatch: null,
    // Optimistic state does not use the eager update optimization.
    lastRenderedReducer: null,
    lastRenderedState: null,
  };
  
  hook.queue = queue;
  // This is different than the normal setState function.
  const dispatch: A => void = (dispatchOptimisticSetState.bind(
    null,
    currentlyRenderingFiber,
    true,// 表示这是一个乐观更新。
    queue,
  ): any);
  queue.dispatch = dispatch;
  
  return [passthrough, dispatch];
}

dispatchOptimisticSetState

React 中 乐观更新(Optimistic Updates) 的核心调度逻辑,主要负责 处理状态更新请求并调度渲染

javascript 复制代码
function dispatchOptimisticSetState<S, A>(
  fiber: Fiber,
  throwIfDuringRender: boolean,
  queue: UpdateQueue<S, A>,
  action: A,
): void {
  const transition = requestCurrentTransition();

  // 创建更新对象
  const update: Update<S, A> = {
    // An optimistic update commits synchronously.
    lane: SyncLane,// 使用同步优先级,确保更新立即执行(UI 即时响应)。
    // After committing, the optimistic update is "reverted" using the same
    // lane as the transition it's associated with.
    revertLane: requestTransitionLane(transition),
    action,// 用户传入的更新动作(如 {type: 'increment'})
    hasEagerState: false,
    eagerState: null,
    next: (null: any),
  };

  // 禁止在渲染过程中更新状态
  if (isRenderPhaseUpdate(fiber)) {
    // 乐观更新
    if (throwIfDuringRender) {
      throw new Error('Cannot update optimistic state while rendering.');
    } 
    
  } else {
    const root = enqueueConcurrentHookUpdate(fiber, queue, update, SyncLane);
    if (root !== null) {
    
      // startUpdateTimerByLane(SyncLane);
      // 触发 React 的调度流程,使用 SyncLane 确保同步执行。
      scheduleUpdateOnFiber(root, fiber, SyncLane);
    }
  }

 // markUpdateInDevTools(fiber, SyncLane, action);
}

二、updateOptimistic

React 中 useOptimistic Hook 的 更新阶段 实现,主要用于 处理组件重新渲染时的乐观状态更新逻辑

javascript 复制代码
function updateOptimistic<S, A>(
  passthrough: S,
  reducer: ?(S, A) => S,
): [S, (A) => void] {
  const hook = updateWorkInProgressHook();
  
  return updateOptimisticImpl(
    hook,
    ((currentHook: any): Hook),
    passthrough,// 用户传入的当前状态值。
    reducer,// 用于处理状态更新的 reducer 函数
  );
}

updateOptimisticImpl

updateOptimisticImpl 的主要作用是:

  1. 同步基础状态 :将当前传入的状态值(passthrough)同步到 Hook 的 baseState
  2. 解析 reducer 函数:确定用于处理状态更新的 reducer。
  3. 委托更新逻辑 :调用 updateReducerImpl 处理具体的状态更新。
javascript 复制代码
function updateOptimisticImpl<S, A>(
  hook: Hook,
  current: Hook | null,
  passthrough: S,
  reducer: ?(S, A) => S,
): [S, (A) => void] {
  // 同步基础状态
  hook.baseState = passthrough;

  // 解析reducer函数
  const resolvedReducer: (S, A) => S =
    typeof reducer === 'function' ? reducer : (basicStateReducer: any);

  return updateReducerImpl(hook, ((currentHook: any): Hook), resolvedReducer);
}
javascript 复制代码
function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
  return typeof action === 'function' ? action(state) : action;
}

流程图

关于查看mountWorkInProgressHook和updateWorkInProgressHook之前文章查看React19源码系列之Hooks(useId)_react useid-CSDN博客

相关推荐
袁煦丞11 分钟前
Moments你的专属无广告树洞:cpolar内网穿透实验室第521个成功挑战
前端·程序员·远程工作
独立开阀者_FwtCoder18 分钟前
Three.js 高效的射线投射和空间查询
前端·javascript·vue.js
bin915322 分钟前
DeepSeek 助力 Vue3 开发:打造丝滑的日历(Calendar),日历_项目里程碑示例(CalendarView01_22)
前端·javascript·vue.js·ecmascript·deepseek
独立开阀者_FwtCoder30 分钟前
Three.js 虫洞特效
前端·vue.js·面试
Tipriest_33 分钟前
基于 Web 的 3D 设计工具Spline介绍
前端·3d·3d设计·spine·网页制作
网络安全小吗喽37 分钟前
靶场(二十五)---小白心得&&靶场体会---Access
服务器·windows·测试工具·网络安全·靶机
伍哥的传说40 分钟前
前端适配方案之 flexible.js 到 postcss-px-to-viewport-8-plugin插件演进
开发语言·前端·javascript·小程序·ecmascript·postcss
江城开朗的豌豆1 小时前
Vue中的.sync修饰符:让父子组件'悄悄话'变得更简单!
前端·javascript·vue.js
深一海1 小时前
import { Add, Dongdong, UserAdd } from ‘@nutui/icons-react‘ 使用图标导入库报错
前端·react.js·前端框架
挑战者6668881 小时前
react小白学习快速上手
前端·学习·react.js