useState的使用
1、简单基础用法:
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、当初始值需要通过复杂计算获得时,可以传入一个函数:
function ExpensiveComponent() {
// 只会在组件初始化时执行一次
const [data, setData] = useState(() => {
return expensiveComputation(); // 复杂计算
});
// ...
}
3、对于对象或数组状态,需要更新部分值:
function Form() {
const [formData, setFormData] = useState({
name: '',
email: ''
});
const handleChange = (e) => {
// 合并更新部分字段
setFormData(prev => ({
...prev,
[e.target.name]: e.target.value
}));
};
// ...
}
useState执行的流程图
初始化入口
useState
函数是 React 中用于在函数组件中添加状态的重要 Hook。它的主要作用是为函数组件引入状态管理能力,允许组件在渲染过程中拥有和更新状态值。该函数接收一个初始状态值(可以是一个值或者一个返回值的函数),并返回一个包含当前状态值和用于更新状态的调度函数的数组。
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(() => [])
,在这种情况下,只有在组件首次渲染时才会调用这个函数,函数的返回值将作为初始状态。这样做的好处是可以避免在每次渲染时都重新计算初始状态(因为函数组件在每次更新时都会重新执行)。
resolveDispatcher
函数的主要作用是解析并返回当前 React 环境中的调度器(dispatcher)。调度器在 React 中扮演着至关重要的角色,它负责管理组件的更新、渲染调度等任务,确保 React 应用能够高效、有序地运行。
function resolveDispatcher() {
// 从 ReactSharedInternals 对象中获取名为 H 的属性,并将其赋值给变量 dispatcher。ReactSharedInternals 是 React 内部使用的一个共享对象,它包含了一些 React 运行时的关键信息和工具。H 属性在这里代表着当前 React 环境下的调度器实例。
const dispatcher = ReactSharedInternals.H;
// 将获取到的调度器实例 dispatcher 返回给调用者。这样,调用 resolveDispatcher 函数的代码就可以使用这个调度器来执行各种调度相关的操作,比如调用调度器的 useState 方法来处理状态管理。
return dispatcher;
}
useState(initialState) {
// 在开发环境中,将当前正在使用的钩子名称设置为 'useState'。
currentHookNameInDev = 'useState';
// 调用 mountHookTypesDev 函数,这个函数可能用于记录和管理当前组件中使用的钩子类型,同样是为了开发调试时能更好地跟踪和分析钩子的使用情况。
// mountHookTypesDev();
// 保存当前的调度器实例到 prevDispatcher 变量中。ReactSharedInternals.H 是 React 内部用于存储调度器的属性。
const prevDispatcher = ReactSharedInternals.H;
// 调度器替换为 InvalidNestedHooksDispatcherOnMountInDEV。这是为了在开发环境中防止在 useState 挂载过程中出现嵌套钩子调用的错误。如果在这个过程中错误地调用了其他钩子,会触发相应的错误提示。
ReactSharedInternals.H = InvalidNestedHooksDispatcherOnMountInDEV;
try {
return mountState(initialState);
} finally {
// ReactSharedInternals.H = prevDispatcher;
}
},
mountState(初始化)
mountState
函数是 React 中用于在组件挂载阶段初始化 useState
钩子的核心函数。useState
是 React 函数式组件中用于管理状态的重要钩子,mountState
函数的主要任务是创建并初始化状态钩子,为状态管理提供必要的基础设施,包括创建状态钩子实例、设置状态更新队列和返回状态值及更新状态的方法。
函数参数含义:
initialState
:初始状态值,可以是状态的具体值,也可以是一个返回状态值的函数。如果传入的是函数,React 会在初始化状态时调用该函数并使用其返回值作为初始状态。
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];
}
hook钩子对象结构:
初始化流程图:
mountStateImpl
mountStateImpl
函数是 React 内部用于在函数组件挂载时初始化状态的关键函数,它是 useState
底层实现的一部分。其核心功能包括创建新的 Hook
实例、处理初始状态(如果初始状态是函数则执行它)、初始化 Hook
的状态属性,并且为 Hook
配置一个更新队列,最后返回初始化好的 Hook
。
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
函数是 React 中处理 setState
操作的关键函数之一。它的主要作用是为 setState
操作分配一个更新优先级车道(lane
),然后调用内部函数 dispatchSetStateInternal
来处理更新的调度,并且在成功调度更新的情况下,启动一个更新定时器。
函数参数含义:
fiber
:当前的Fiber
节点。Fiber
是 React 16 及以后版本中引入的协调算法的基础数据结构,代表了组件树中的一个节点。queue
:更新队列,用于存储setState
操作的相关信息。每个Fiber
节点都有一个对应的更新队列,用于管理该节点的状态更新。action
:setState
操作传入的动作,通常是一个对象或函数。例如,setState({ count: 1 })
中的{ count: 1 }
就是一个动作对象;setState(prevState => ({ count: prevState.count + 1 }))
中的prevState => ({ count: prevState.count + 1 })
就是一个动作函数。
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
函数是 React 中处理 setState
操作的内部核心函数,它的主要任务是将状态更新请求(action
)封装成一个 Update
对象,并根据不同情况处理这个更新请求,包括在渲染阶段处理更新、尝试提前计算新状态以避免不必要的更新,以及将更新加入队列并调度更新等操作。最后返回一个布尔值,表示是否成功调度了更新。
函数参数含义:
fiber
:当前执行更新操作的Fiber
节点。Fiber
是 React 协调算法的基础数据结构,包含了组件的状态、属性等信息。queue
:更新队列,存储了与setState
操作相关的信息,如之前的状态、reducer
函数等。action
:setState
操作传入的动作,通常是一个值或函数,用于更新状态。lane
:更新的优先级车道,用于确定更新的执行顺序,不同的车道对应不同的优先级。
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
节点的更新是否发生在渲染阶段。
function isRenderPhaseUpdate(fiber: Fiber): boolean {
const alternate = fiber.alternate;
return (
fiber === currentlyRenderingFiber ||
(alternate !== null && alternate === currentlyRenderingFiber)
);
}
enqueueRenderPhaseUpdate
enqueueRenderPhaseUpdate
函数的主要作用是处理渲染阶段的更新操作。在 React 中,当更新发生在渲染阶段时,会调用这个函数将更新操作暂存到一个更新队列中,以便在当前渲染阶段结束后,重新启动渲染并应用这些暂存的更新到正在处理的 Hook
上。
函数参数含义:
queue
:类型为UpdateQueue<S, A>
,表示Hook
的更新队列,用于存储与状态更新相关的信息,包括之前的状态、更新的动作等。update
:类型为Update<S, A>
,代表具体的更新操作对象,包含了更新的优先级车道(lane
)、回滚车道(revertLane
)、更新动作(action
)、是否有提前计算的状态(hasEagerState
)以及提前计算的状态值(eagerState
)等信息。
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;
}
enqueueConcurrentHookUpdateAndEagerlyBailout
React 中处理 并发 Hook 更新 的特殊优化路径,主要用于 跳过优先级调度 和 立即处理更新。
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();
}
}
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 并发更新机制中 完成更新队列处理 的核心函数,主要负责 将暂存的更新合并到队列 和 标记优先级。
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);
}
}
}
enqueueConcurrentHookUpdate
enqueueConcurrentHookUpdate
函数主要用于将一个并发的 Hook 更新操作加入到更新队列中,并且返回更新所关联的 FiberRoot
。
函数参数含义
fiber
:当前执行更新操作的Fiber
节点。queue
:Hook
的更新队列,类型为HookQueue<S, A>
,其中S
表示状态的类型,A
表示动作(action)的类型。更新队列用于存储一系列的更新操作。update
:具体的Hook
更新操作,类型为HookUpdate<S, A>
,包含了更新所需的信息,如更新的动作、优先级等。lane
:更新的优先级车道(Lane
)。在 React 的调度系统中,不同的更新会被分配到不同的车道,车道决定了更新的优先级,高优先级的车道会优先被处理。
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 更新队列管理的核心函数,主要处理 更新入队 和 优先级标记。
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);
}
}
const concurrentQueues: Array<any> = [];
let concurrentQueuesIndex = 0;
let concurrentlyUpdatedLanes: Lanes = NoLanes;
concurrentQueues = [
fiber1, queue1, update1, lane1,
fiber2, queue2, update2, lane2,
// ...
];
工具函数之 isUnsafeClassRenderPhaseUpdate
React 用于 检测不安全的类组件渲染阶段更新 的辅助函数,主要用于 开发模式下的警告提示。
function isUnsafeClassRenderPhaseUpdate(fiber: Fiber): boolean {
return (executionContext & RenderContext) !== NoContext;
}
工具函数之 unsafe_markUpdateLaneFromFiberToRoot
React 中用于 标记更新优先级 的核心函数,主要在 同步更新 场景下使用。
function unsafe_markUpdateLaneFromFiberToRoot(
sourceFiber: Fiber,
lane: Lane,
): FiberRoot | null {
const root = getRootForUpdatedFiber(sourceFiber);
markUpdateLaneFromFiberToRoot(sourceFiber, null, lane);
return root;
}
工具函数之 markUpdateLaneFromFiberToRoot
React 中用于标记更新优先级并在 Fiber 树中传播的核心函数,主要作用是将更新的优先级从当前 Fiber 节点向上传播到根节点,确保整个更新流程的优先级管理。
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) 的核心函数,主要用于 暂存离屏组件的更新,避免不必要的渲染。
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;
}
root.hiddenUpdates = [
[/* 优先级0的更新数组 */],
[/* 优先级1的更新数组 */],
// ...
];
function laneToIndex(lane: Lane) {
return pickArbitraryLaneIndex(lane);
}
updateState(更新入口)
updateState
函数是 React 内部用于更新状态的函数,它本质上是对 updateReducer
函数的封装。该函数借助 basicStateReducer
这个基础的状态归约函数,以一种更简洁的方式实现状态更新逻辑,其功能与 useState
类似,旨在为函数组件提供状态管理能力。
function updateState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
return updateReducer(basicStateReducer, initialState);
}
工具函数之 basicStateReducer
basicStateReducer
是 React 中用于处理状态更新的基础归约函数,通常在 useState
钩子的内部实现里被使用。它接收当前状态 state
和一个动作 action
作为参数,根据 action
的类型来决定如何更新状态,并返回更新后的状态。
function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
return typeof action === 'function' ? action(state) : action;
}
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);
}
updateReducerImpl
updateReducerImpl
函数是 React 中用于处理 useReducer
钩子状态更新的核心函数。它的主要任务是根据传入的 reducer
函数,处理 Hook
对象中的更新队列,计算出新的状态,并更新 Hook
和相关队列的属性。最终,该函数返回一个包含新状态和状态更新调度函数的数组。
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
)是否不同来确定当前更新是否为隐藏更新。如果updateLane
与update.lane
不同,说明原始的更新车道里包含OffscreenLane
,此更新为隐藏更新。
// 根据是否为隐藏更新来决定是否应跳过当前更新。
const shouldSkipUpdate = isHiddenUpdate
// getWorkInProgressRootRenderLanes() 会返回当前正在处理的根节点的渲染车道
? !isSubsetOfLanes(getWorkInProgressRootRenderLanes(), updateLane)
: !isSubsetOfLanes(renderLanes, updateLane);
// 上述代码,updateLane不是渲染车道的子集,则隐藏更新
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);
}
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);
}
}