分为三个阶段
我们首先来看源码
export function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
我们接着看 resolveDispatcher,会发现 resolveDispatcher 是 null,回顾 react Fiber 中的流程,在 beginWork 中,有执行 functionComponent 的方法,去那里看
// updateFunctionComponent
renderWithHooks(
current,
workInProgress,
Component,
nextProps,
context,
renderLanes,
);
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;
}
可以看到,如果是 mount 阶段,返回 HooksDispatcherOnMount,如果是 update 阶段,返回 HooksDispatcherOnUpdate。
function mountState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const hook = mountWorkInProgressHook();
if (typeof initialState === 'function') {
// $FlowFixMe: Flow doesn't like mixed types
initialState = initialState();
}
hook.memoizedState = hook.baseState = initialState;
const queue = (hook.queue = {
pending: null,
interleaved: null,
lanes: NoLanes,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: (initialState: any),
});
const dispatch: Dispatch<
BasicStateAction<S>,
> = (queue.dispatch = (dispatchAction.bind(
null,
currentlyRenderingFiber,
queue,
): any));
return [hook.memoizedState, dispatch];
}
function dispatchAction<S, A>(
fiber: Fiber,
queue: UpdateQueue<S, A>,
action: A,
) {
const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
}
执行 dispatchAction,我们可以看到,在 dispatchAction 中,又执行了 scheduleUpdateOnFiber,scheduleUpdateOnFiber 是 scheduler 中的,如果再次执行 scheduleUpdateOnFiber,那么下次执行到 beginWork 中,ReactCurrentDispatcher.current 将不等于 null,则会执行 HooksDispatcherOnUpdate。
当再次执行 HooksDispatcherOnUpdate,触发 updateState。
function updateState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
return updateReducer(basicStateReducer, (initialState: any));
}
updateReducer 中,判断当前是否有任务存在,如果有,将值放在 eagerState 上,将更改的值赋给 newState
function updateReducer<S, I, A>(
reducer: (S, A) => S,
initialArg: I,
init?: I => S,
): [S, Dispatch<A>] {
do{
// ...
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 {
const action = update.action;
newState = reducer(newState, action);
}
}while(update !== null && update !== first)
}
我们可以看 reactHook.js 中的代码,发现 hooks 调用的方法都几乎相同,只是传参不同。与上面的流程一样,都是 mount、dispatchAction、update
export function useEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
const dispatcher = resolveDispatcher();
return dispatcher.useEffect(create, deps);
}
我们直接看 mountEffect
function mountEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
// 非Dev
return mountEffectImpl(
PassiveEffect | PassiveStaticEffect,
HookPassive,
create,
deps,
);
}
执行 mountEffectImpl
function updateEffectImpl(fiberFlags, hookFlags, create, deps): void {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
let destroy = undefined;
if (currentHook !== null) {
const prevEffect = currentHook.memoizedState;
destroy = prevEffect.destroy;
if (nextDeps !== null) {
const prevDeps = prevEffect.deps;
if (areHookInputsEqual(nextDeps, prevDeps)) {
hook.memoizedState = pushEffect(hookFlags, create, destroy, nextDeps);
return;
}
}
}
currentlyRenderingFiber.flags |= fiberFlags;
hook.memoizedState = pushEffect(
HookHasEffect | hookFlags,
create,
destroy,
nextDeps,
);
}
我们可以看到,将当前的 hookFlag 和 create(function Component)传入 pushEffect 执行,并将返回值赋值给 memoizedState,就是我们在 useState 中提到的初始值,其中,destroy 就是 useEffect 中返回的函数,即 componentWillUnMount
function pushEffect(tag, create, destroy, deps) {
const effect: Effect = {
tag,
create,
destroy,
deps,
// Circular
next: (null: any),
};
let componentUpdateQueue: null | FunctionComponentUpdateQueue = (currentlyRenderingFiber.updateQueue: any);
if (componentUpdateQueue === null) {
componentUpdateQueue = createFunctionComponentUpdateQueue();
currentlyRenderingFiber.updateQueue = (componentUpdateQueue: any);
componentUpdateQueue.lastEffect = effect.next = effect;
} else {
const lastEffect = componentUpdateQueue.lastEffect;
if (lastEffect === null) {
componentUpdateQueue.lastEffect = effect.next = effect;
} else {
const firstEffect = lastEffect.next;
lastEffect.next = effect;
effect.next = firstEffect;
componentUpdateQueue.lastEffect = effect;
}
}
return effect;
}
看 pushEffect,我们就可以理解,为什么子组件的 useeffect 总是会先执行
之后,我们在 react Render 流程 commit 中,dom 更新完才会执行 useEffects。在源码中,执行 create(),得到返回函数。
最后,在 commitHookEffectListUnmount 中的 safelyCallDestroy 执行 destroy。
function commitHookEffectListUnmount(
flags: HookFlags,
finishedWork: Fiber,
nearestMountedAncestor: Fiber | null,
) {
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
if (lastEffect !== null) {
const firstEffect = lastEffect.next;
let effect = firstEffect;
do {
if ((effect.tag & flags) === flags) {
// Unmount
const destroy = effect.destroy;
effect.destroy = undefined;
if (destroy !== undefined) {
safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy);
}
}
effect = effect.next;
} while (effect !== firstEffect);
}
}
基于上面的经验,直接看 mountMemo
function mountMemo<T>(
nextCreate: () => T,
deps: Array<mixed> | void | null,
): T {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
先执行 mountWorkInProgressHook,在 mountWorkInProgressHook 中,得到 hook 对象,接着,判断是否首次更新,返回缓存好的 workInProgressHook。在 mountMemo 中接着执行传入的 callback,返回 state
function mountWorkInProgressHook(): Hook {
const hook: Hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null,
};
if (workInProgressHook === null) {
// This is the first hook in the list
currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
} else {
// Append to the end of the list
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}
在 updateMemo 中,判断上次 useMemo 值不为空,并且 deps 不为空,则进行浅比较。updateWorkInProgressHook 就不看了,还是获得 hook 对象。
function updateMemo<T>(
nextCreate: () => T,
deps: Array<mixed> | void | null,
): T {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
// Assume these are defined. If they're not, areHookInputsEqual will warn.
if (nextDeps !== null) {
const prevDeps: Array<mixed> | null = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
}
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
areHookInputsEqual 中,进行了浅比较,循环 dep 数组,判断 dep 是否变更,如果没有变更,则返回上一次 state 的值。
function areHookInputsEqual(
nextDeps: Array<mixed>,
prevDeps: Array<mixed> | null,
) {
// 去掉无用代码。。。
for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
if (is(nextDeps[i], prevDeps[i])) {
continue;
}
return false;
}
return true;
}
这里就不说了,唯一的区别就是,传入的是 function,且 dep 没有变化,不需要执行 callback。。。