useState
useState是React的众多hooks之一,在react包中的ReactHooks文件中定义:
export function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
可以看到函数主要是调用了resolveDispatcher()返回的useState方法,实际上resolveDispatcher()返回的是ReactCurrentDispatcher.current。
来看一下ReactCurrentDispatcher.current的值是什么?
对函数的赋值发生在renderWithHooks函数中我们省略其他处理得到:
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
当初始化hook的过程中会先调用HooksDispatcherOnMount,后续更新过程中则会调用HooksDispatcherOnUpdate
初始化State :mountState
通过调用堆栈结合代码可以看出mount阶段
HooksDispatcherOnMount中的useState指向的是mountState函数
function mountState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const hook = mountWorkInProgressHook();
if (typeof initialState === 'function') {
initialState = initialState();
}
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;
const dispatch: Dispatch<
BasicStateAction<S>,
> = (queue.dispatch = (dispatchSetState.bind(
null,
currentlyRenderingFiber,
queue,
): any));
return [hook.memoizedState, dispatch];
}
函数中进行如下操作:
1. 生成hook对象:
hook对象通过mountWorkInProgressHook函数生成:
function mountWorkInProgressHook(): Hook {
const hook: Hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null,
};
if (workInProgressHook === null) {
currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
} else {
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}
函数中首先初始化了一个Hook实例,然后对当前Fiber的workInProgressHook进行判断,如果是null,则将生成的hook对象挂载到当前Fiber的memoizedState上,用于存储Fiber节点对应的useState hook的初始值,并给workInProgressHook赋值。如果已经初始化过,则将hook赋值给workInProgressHook和workInProgressHook.next形成一个链表结构。
2.初始化state的值
hook.memoizedState = hook.baseState = initialState;
3.生成一个udateQueue
const queue: UpdateQueue<S, BasicStateAction<S>> = {
pending: null,
lanes: NoLanes,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: (initialState: any),
};
UpdateQueue是React中用于管理和处理状态更新的数据结构
其中:
export type UpdateQueue<S, A> = {
pending: Update<S, A> | null,
lanes: Lanes,
dispatch: (A => mixed) | null,
lastRenderedReducer: ((S, A) => S) | null,
lastRenderedState: S | null,
};
pending: 指向当前待处理的更新对象lanes: lane优先级集合dispatch分发函数,它允许从外部向这个更新队列调度新的更新lastRenderedReducer最后一次渲染时使用的reducer函数,用于根据当前状态和动作计算新的状态lastRenderedState最后一次渲染时的状态
4.初始化dispatch
dispatch实际上上是使用了bind的dispatchSetState函数,该函数会在更新state的方法被调用时执行
5.返回
函数最后会返回hook中储存的当前状态和dispatch方法,就是我们在使用useState时的两个返回值
const [xxx, setXXX] = useState()
修改state : dispatchSetState
当需要改变state的值时dispatchSetState会被调用
来看一下函数做了什么事情:
function dispatchReducerAction<S, A>(
fiber: Fiber,
queue: UpdateQueue<S, A>,
action: A,
) {
const lane = requestUpdateLane(fiber);
const update: Update<S, A> = {
lane,
action,
hasEagerState: false,
eagerState: null,
next: (null: any),
};
if (isRenderPhaseUpdate(fiber)) {
enqueueRenderPhaseUpdate(queue, update);
} else {
const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
if (root !== null) {
const eventTime = requestEventTime();
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
entangleTransitionUpdate(root, queue, lane);
}
}
markUpdateInDevTools(fiber, lane, action);
}
function dispatchSetState<S, A>(
fiber: Fiber,
queue: UpdateQueue<S, A>,
action: A,
) {
const lane = requestUpdateLane(fiber);
const update: Update<S, A> = {
lane,
action,
hasEagerState: false,
eagerState: null,
next: (null: any),
};
if (isRenderPhaseUpdate(fiber)) {
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;
try {
const currentState: S = (queue.lastRenderedState: any);
const eagerState = lastRenderedReducer(currentState, action);
update.hasEagerState = true;
update.eagerState = eagerState;
if (is(eagerState, currentState)) {
enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update);
return;
}
} catch (error) {
} finally {
}
}
}
const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
if (root !== null) {
const eventTime = requestEventTime();
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
entangleTransitionUpdate(root, queue, lane);
}
}
markUpdateInDevTools(fiber, lane, action);
}
-
调用
requestUpdateLane,用于获取fiber更新的优先级 -
调用
isRenderPhaseUpdate用于判断是否是render阶段产生的更新,如果是的话会将update对象加到,updateQueue的pending链表之上 -
进行
eagerState优化策略处理:
通过fiber.lanes是不是NoLanes,判断目前fiberNode是否有待处理的更新,如果没有则计算eagerState, 函数内部首先获取了lastRenderedReducer,在setState中其为basicStateReducer,可以看到内部执行了action或者直接赋值function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S { return typeof action === 'function' ? action(state) : action; }在执行了
lastRenderedReducer后将得到的值和currentState进行比较(is(eagerState, currentState)),如果值没有变化,则不再需要进入scheduler。 -
如果没有命中
eagerState优化策略,则调用enqueueConcurrentHookUpdateexport 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); return getRootForUpdatedFiber(fiber); }在
enqueueUpdate中更新被加入到队列之中
消费队列中的update
1.处理更新队列
在dispatchSetState的最后,scheduleUpdateOnFiber进行调度更新,在render阶段,函数调用至
prepareFreshStack ,其中会调用finishQueueingConcurrentUpdates函数,对更新队列进行处理
export function finishQueueingConcurrentUpdates(): void {
const endIndex = concurrentQueuesIndex;
concurrentQueuesIndex = 0;
concurrentlyUpdatedLanes = NoLanes;
let i = 0;
while (i < endIndex) {
const fiber: Fiber = concurrentQueues[i];
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);
}
}
}
通过处理会使得pending始终指向最后一个update,最后一个update通过next又能指向第一个update
2. 更新组件过程中会再次调用useState
具体调度渲染过程不再阐述,来看一下调用堆栈:
可以看到在渲染过程中调用的useState实际上执行的是updateReducer
function updateReducer<S, I, A>(
reducer: (S, A) => S,
initialArg: I,
init?: I => S,
): [S, Dispatch<A>] {
const hook = updateWorkInProgressHook();
const queue = hook.queue;
queue.lastRenderedReducer = reducer;
const current: Hook = (currentHook: any);
let baseQueue = current.baseQueue;
const pendingQueue = queue.pending;
if (pendingQueue !== null) {
if (baseQueue !== null) {
const baseFirst = baseQueue.next;
const pendingFirst = pendingQueue.next;
baseQueue.next = pendingFirst;
pendingQueue.next = baseFirst;
}
current.baseQueue = baseQueue = pendingQueue;
queue.pending = null;
}
if (baseQueue !== null) {
const first = baseQueue.next;
let newState = current.baseState;
let newBaseState = null;
let newBaseQueueFirst = null;
let newBaseQueueLast = null;
let update = first;
do {
const updateLane = removeLanes(update.lane, OffscreenLane);
const isHiddenUpdate = updateLane !== update.lane;
const shouldSkipUpdate = isHiddenUpdate
? !isSubsetOfLanes(getWorkInProgressRootRenderLanes(), updateLane)
: !isSubsetOfLanes(renderLanes, updateLane);
if (shouldSkipUpdate) {
const clone: Update<S, A> = {
lane: updateLane,
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,
updateLane,
);
markSkippedUpdateLanes(updateLane);
} else {
if (newBaseQueueLast !== null) {
const clone: Update<S, A> = {
lane: NoLane,
action: update.action,
hasEagerState: update.hasEagerState,
eagerState: update.eagerState,
next: (null: any),
};
newBaseQueueLast = newBaseQueueLast.next = clone;
}
if (update.hasEagerState) {
newState = ((update.eagerState: any): S);
} else {
const action = update.action;
newState = reducer(newState, action);
}
}
update = update.next;
} while (update !== null && update !== first);
if (newBaseQueueLast === null) {
newBaseState = newState;
} else {
newBaseQueueLast.next = (newBaseQueueFirst: any);
}
if (!is(newState, hook.memoizedState)) {
markWorkInProgressReceivedUpdate();
}
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];
}
函数主要流程:
- 调用
updateWorkInProgressHook获取了我们在初始化阶段挂载在Fiber上的hook实例。 - 判断
pendingQueue是否为null, 如果存在并且baseQueue不为null,将baseQueue和pendingQueue相连,相连后作为新的baseQueue,保存到当前Fiber。 - 判断是否是需要跳过的更新,判断更新的优先级中是否有
OffscreenLane优先级,并且进行判断检查当前update的lane属不属于render阶段的更新,不是则跳过处理过程,如果属于则尝试从hasEagerState中取值,或者使用action计算新值,对state的值进行更新。 - 最后返回新的
state和dispatch方法。
总结
本文解析了React中useState Hook的实现,包括其在组件挂载和更新时的行为。详细介绍了useState如何创建和管理状态,以及如何通过dispatchSetState处理状态更新。最后,探讨了组件更新时如何消费更新队列,确保状态正确更新。