useState
eg: const [count, setCount] = React.useState(0);
入口
export function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
function resolveDispatcher() {
const dispatcher = ReactCurrentDispatcher.current;
return dispatcher;
}
useState挂在dispatcher上,resolveDispatcher() 返回的是 ReactCurrentDispatcher.current, ReactCurrentDispatcher 是什么呢?
import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes';
/**
* Keeps track of the current dispatcher.
*/
const ReactCurrentDispatcher = {
/**
* @internal
* @type {ReactComponent}
*/
current: (null: null | Dispatcher),
};
React Hooks 的渲染核心是renderWithHooks,在renderWithHooks函数中,
export function renderWithHooks<Props, SecondArg>(
current: Fiber | null,
workInProgress: Fiber,
Component: (p: Props, arg: SecondArg) => any,
props: Props,
secondArg: SecondArg,
nextRenderLanes: Lanes,
): any {
// ....
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
// .....
const HooksDispatcherOnMount: Dispatcher = {
readContext,
// ...
useCallback: mountCallback,
useContext: readContext,
useEffect: mountEffect,
useMemo: mountMemo,
useState: mountState,
// ...
};
const HooksDispatcherOnUpdate: Dispatcher = {
readContext,
// ...
useCallback: updateCallback,
useContext: readContext,
useEffect: updateEffect,
useMemo: updateMemo,
useRef: updateRef,
useState: updateState,
// ....
};
}
- 当挂载时,ReactCurrentDispatcher.current为HooksDispatcherOnMount,更新时,为HooksDispatcherOnUpdate。在调用useState时,分别调用的不同的函数。挂载时,首先会调用mountWorkInProgressHook函数,生成hooks对象。
从hooks的生成过程中可以看出:hook其实是以链表的形式存储起来的。每一个hook都有一个指向下一个hook的指针。所以在开发时,hook要按照固定的顺序来,不能在判断语句中使用hook。
function mountWorkInProgressHook() {
// 初始化的hook对象
var hook = {
memoizedState: null, // 存储更新后的state值
baseState: null, // 存储更新前的state
baseQueue,
queue: null, // UpdateQueue
next: null
};
// workInProgressHook是一个全局变量,表示当前正在处理的hook
// 如果workInProgressHook链表为null就将新建的hook对象赋值给它,如果不为null,那么就加在链表尾部。
if (workInProgressHook === null) {
currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
} else {
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}
生成hook对象之后,会对hook对象进行初始化。以下是具体流程:
function mountState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
// 生成hook对象
const hook = mountWorkInProgressHook();
if (typeof initialState === 'function') {
initialState = initialState();
}
hook.memoizedState = hook.baseState = initialState; // 第二步:获取初始值并初始化hook对象
const queue = hook.queue = {
// 保存 update 对象
pending: null,
// 保存dispatchAction.bind()的值
dispatch: null,
// 一次新的dispatch触发前最新的reducer
// useState 保存固定函数: 可以理解为一个react 内置的reducer
// (state, action) => { return typeof action === 'function' ? action(state) : action }
lastRenderedReducer: reducer
// 一次新的dispatch触发前最新的state
lastRenderedState: (initialState: any),
}
// 绑定当前 fiber 和 queue.
const dispatch: Dispatch<
BasicStateAction<S>,
> = (queue.dispatch = (dispatchAction.bind(
null,
currentlyRenderingFiber,
queue,
): any));
// 返回当前状态和修改状态的方法
return [hook.memoizedState, dispatch];
}
整个初始化完成之后,setCount又是怎么对count值进行更新的呢?实际上就是通过dispatchAction方法进行更新的。
// currentlyRenderingFiber$1是一个全局变量,表示当前正在渲染的FiberNode
var dispatch = queue.dispatch = dispatchAction.bind(null, currentlyRenderingFiber$1, queue);
function dispatchAction(fiber,queue,action) {
// ...
// 1. 创建update对象
// 该对象保存的是调度优先级/state/reducer以及用户调用dispatch/setState 时传入的action
const update: Update<S, A> = {
lane,
action,
eagerReducer: null,
eagerState: null,
next: (null: any),
};
// 2. 将update更新到queue.pending中,最后的update.next 指向第一个update对象,形成一个闭环。
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;
}
可以知道,pending中存储了所有的update,但是state值还没有改变,只是被缓存起来了。state值会在下一次render时,进行更新。
- 在更新时,updateState实际上调用的是updateReducer函数。
function updateReducer<S, I, A>(
reducer: (S, A) => S,
initialArg: I,
init?: I => S,
): [S, Dispatch<A>] {
// 获取当前的hook
const hook = updateWorkInProgressHook();
const queue = hook.queue;
// 更新 reducer
queue.lastRenderedReducer = reducer;
const current: Hook = (currentHook: any);
// fiber 渲染后,尚未执行完成 update 更新,存于 currentFiber
let baseQueue = current.baseQueue;
// 最后一次的update对象
const pendingQueue = queue.pending;
if (pendingQueue !== null) {
// 将未完成update更新插入到链表的头部。
if (baseQueue !== null) {
// Merge the pending queue and the base queue.
const baseFirst = baseQueue.next;
const pendingFirst = pendingQueue.next;
baseQueue.next = pendingFirst;
pendingQueue.next = baseFirst;
}
current.baseQueue = baseQueue = pendingQueue;
queue.pending = null;
}
if (baseQueue !== null) {
// We have a queue to process.
const first = baseQueue.next;
let newState = current.baseState;
let newBaseState = null;
let newBaseQueueFirst = null;
let newBaseQueueLast = null;
let update = first;
// 调度,获取最新的state
do {
const updateLane = update.lane;
if (!isSubsetOfLanes(renderLanes, updateLane)) {
const clone: Update<S, A> = {
lane: updateLane,
action: update.action,
eagerReducer: update.eagerReducer,
eagerState: update.eagerState,
next: (null: any),
};
if (newBaseQueueLast === null) {
newBaseQueueFirst = newBaseQueueLast = clone;
newBaseState = newState;
} else {
newBaseQueueLast = newBaseQueueLast.next = clone;
}
// Update the remaining priority in the queue.
// TODO: Don't need to accumulate this. Instead, we can remove
// renderLanes from the original lanes.
currentlyRenderingFiber.lanes = mergeLanes(
currentlyRenderingFiber.lanes,
updateLane,
);
markSkippedUpdateLanes(updateLane);
} else {
// This update does have sufficient priority.
if (newBaseQueueLast !== null) {
const clone: Update<S, A> = {
// This update is going to be committed so we never want uncommit
// it. Using NoLane works because 0 is a subset of all bitmasks, so
// this will never be skipped by the check above.
lane: NoLane,
action: update.action,
eagerReducer: update.eagerReducer,
eagerState: update.eagerState,
next: (null: any),
};
newBaseQueueLast = newBaseQueueLast.next = clone;
}
// 对应 dispatchAction 函数中的 update.eagerReducer 赋值。
if (update.eagerReducer === reducer) {
// If this update was processed eagerly, and its reducer matches the
// current reducer, we can use the eagerly computed state.
newState = ((update.eagerState: any): S);
} else {
// 否则计算最新的state
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);
}
// Mark that the fiber performed work, but only if the new state is
// different from the current state.
if (!is(newState, hook.memoizedState)) {
markWorkInProgressReceivedUpdate();
}
hook.memoizedState = newState;
hook.baseState = newBaseState;
hook.baseQueue = newBaseQueueLast;
queue.lastRenderedState = newState;
}
const dispatch: Dispatch<A> = (queue.dispatch: any);
return [hook.memoizedState, dispatch];
}
// updateState做的事情,实际上就是拿到更新队列,循环队列,并根据每一个update对象对当前hook进行状态更新。最后返回最终的结果。
function updateState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
return updateReducer(basicStateReducer, (initialState: any));
}
React通过单链表来存储hooks。在执行useState函数时,每个hook节点通过循环链表存储所有的更新操作,在update阶段会依次执行update循环链表中的所有更新操作,最终拿到最新的state并返回。