useState是React Hook中最常用的一个函数,它负责在函数组件中管理状态。
useState函数的声明:
复制代码
function useState(initialState) {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
useState实际上是通过resolveDispatcher获取了dispatcher对象,并调用了其useState方法。那么,dispatcher对象是个啥呢?
dispatcher是React内部用于处理各种不同类型的Hook函数的对象。在使用每种Hook函数时,都会根据其类型获取相应的dispatcher对象。具体实现方式如下:
复制代码
function resolveDispatcher() {
const fiber = ReactCurrentOwner.current;
return fiber?.stateNode?.__reactInternalMemoizedHookDispatcher || defaultDispatcher;
}
先通过ReactCurrentOwner获取当前组件对应的fiber对象。然后,从fiber对象上的stateNode属性中获取了与此组件相关联的Hook Dispatcher对象。如果找不到,则返回默认的dispatcher对象。
dispatcher中的useState方法是咋干活的?
复制代码
export function useState<S>(initialState: (() => S) | S) {
const [hook, next] = updateWorkInProgressHook();
if (hook.queue === null) {
// 如果该hook没有挂载队列,则初始化一个
const initialStateObject =
typeof initialState === 'function'
? (initialState as (() => S))()
: initialState;
hook.memoizedState = hook.queue = {
// 队列中的第一个节点是当前状态值
pending: null,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: (initialStateObject as S),
};
}
const queue = hook.queue;
const dispatch = queue.dispatch ??= dispatchAction.bind(
null,
currentlyRenderingFiber$1,
queue,
);
if (isUpdatingOpaqueValueInRenderPhaseInDEV) {
// prohibit use of setState inside an updater
disableLogs();
try {
// 当hook正在渲染和执行更新时,如果使用了setState会发出警告
if (hook.isRendering) {
console.error(
'Warning: Cannot update a state variable during rendering. ' +
'You should only render once.',
);
}
if (queue.isCurrentlyProcessingDispatch) {
console.error(
'Warning: Cannot update a state variable while a component is ' +
'rendering. React ensures that your setState ' +
'call never happens during the current render. It will become ' +
'undefined in a future version of React.',
);
}
} finally {
reenableLogs();
}
}
const memoizedState = hook.memoizedState;
let update = queue.pending;
if (update !== null) {
// 如果有状态更新,则进行状态合并
do {
// 更新后的值由多个partialStates组成
const action = update.action;
memoizedState = action(memoizedState);
} while ((update = update.next) !== null);
queue.pending = null;
}
// 将状态更新操作封装为setState函数进行返回
return [
memoizedState,
dispatch as Dispatch<SetStateAction<S>>,
];
}
在useState方法中,通过updateWorkInProgressHook获取当前组件的hook对象。如果该hook对象还没有挂载过队列,则创建一个新的队列,并将当前状态值保存到队列的第一个节点中。
如果有待处理的状态更新,则进行状态合并,根据多个partialStates计算出最终的新状态,并返回给调用方。
useState函数将状态更新操作封装成一个名为setState的函数,并将其作为第二个返回值返回。当我们调用这个函数时,实际上就是向队列中添加一个新的状态更新节点,等待下一次重新渲染时进行状态合并。
useState的实现方式比较清晰简单,主要涉及到的核心概念包括:Hook、Hook Dispatcher以及状态更新队列等。掌握了这些基础之后,就可以更加灵活地使用useState,并深入理解React Hook的运行原理了。