function finishHooks(
Component: any,
props: any,
children: any,
refOrContext: any,
): any {
if (!enableHooks) {
return children;
}
// This must be called after every function component to prevent hooks from
// being used in classes.
// 如果是重复渲染的情况,清空全局变量 currentHook workInProgressHook,并计数 numberOfReRenders 渲染次数,在 dispatchAction 会 判断次数如果大于 RE_RENDER_LIMIT 抛出错误
// 再执行一次 Component(props, refOrContext); 将 重复渲染在一次 更新中执行掉
while (didScheduleRenderPhaseUpdate) {
// Updates were scheduled during the render phase. They are stored in
// the `renderPhaseUpdates` map. Call the component again, reusing the
// work-in-progress hooks and applying the additional updates on top. Keep
// restarting until no more updates are scheduled.
didScheduleRenderPhaseUpdate = false;
numberOfReRenders += 1;
// Start over from the beginning of the list
currentHook = null;
workInProgressHook = null;
componentUpdateQueue = null;
children = Component(props, refOrContext);
}
renderPhaseUpdates = null;
numberOfReRenders = 0;
// 更新中的 fiber 对象
const renderedWork: Fiber = (currentlyRenderingFiber: any);
// firstWorkInProgressHook 是 functionComponent 每一个 hooks 对应的 hook 对象。
renderedWork.memoizedState = firstWorkInProgressHook;
renderedWork.expirationTime = remainingExpirationTime;
// 对于useEffect 或者 useLayoutEffect 生成的 effect 链
renderedWork.updateQueue = (componentUpdateQueue: any);
const didRenderTooFewHooks =
currentHook !== null && currentHook.next !== null;
renderExpirationTime = NoWork;
currentlyRenderingFiber = null;
firstCurrentHook = null;
currentHook = null;
firstWorkInProgressHook = null;
workInProgressHook = null;
remainingExpirationTime = NoWork;
componentUpdateQueue = null;
// Always set during createWorkInProgress
// isReRender = false;
// These were reset above
// didScheduleRenderPhaseUpdate = false;
// renderPhaseUpdates = null;
// numberOfReRenders = 0;
return children;
}
useState
实际使用的是 useReducer
function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
return useReducer(
basicStateReducer,
// useReducer has a special case to support lazy useState initializers
(initialState: any),
);
}
function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
return typeof action === 'function' ? action(state) : action;
}
useReducer
在每个 hooks api 调用时会调用 createWorkInProgressHook 创建一个 hook 对象并赋值给全局变量 workInProgressHook 。
在 createWorkInProgressHook 内部 hook 对象构建成链表结构
function useReducer<S, A>(
reducer: (S, A) => S,
initialState: S,
initialAction: A | void | null,
): [S, Dispatch<A>] {
// 找到正在执行更新的 fiber
currentlyRenderingFiber = resolveCurrentlyRenderingFiber();
// 创建一个 hook 对象, 并通过全局变量构建 hook 链表
workInProgressHook = createWorkInProgressHook();
// 非首次渲染, queue 不为空的情况
let queue: UpdateQueue<A> | null = (workInProgressHook.queue: any);
if (queue !== null) {
// Already have a queue, so this is an update.
// 重复渲染
if (isReRender) {
// This is a re-render. Apply the new render phase updates to the previous
// work-in-progress hook.
const dispatch: Dispatch<A> = (queue.dispatch: any);
if (renderPhaseUpdates !== null) {
// Render phase updates are stored in a map of queue -> linked list
// renderPhaseUpdates 在 dispatchAction 被设置已 queque 为 key 的 map对象
const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
if (firstRenderPhaseUpdate !== undefined) {
// 取出来后 删掉 queue
renderPhaseUpdates.delete(queue);
let newState = workInProgressHook.memoizedState;
let update = firstRenderPhaseUpdate;
// 重复渲染的过程中 所有update 立马执行,不进行 expirationTime 的判断
do {
// Process this render phase update. We don't have to check the
// priority because it will always be the same as the current
// render's.
const action = update.action;
newState = reducer(newState, action);
update = update.next;
} while (update !== null);
// 更新 memoizedState
workInProgressHook.memoizedState = newState;
// Don't persist the state accumlated from the render phase updates to
// the base state unless the queue is empty.
// TODO: Not sure if this is the desired semantics, but it's what we
// do for gDSFP. I can't remember why.
if (workInProgressHook.baseUpdate === queue.last) {
workInProgressHook.baseState = newState;
}
return [newState, dispatch];
}
}
return [workInProgressHook.memoizedState, dispatch];
}
// The last update in the entire queue
const last = queue.last;
// The last update that is part of the base state.
const baseUpdate = workInProgressHook.baseUpdate;
// Find the first unprocessed update.
let first;
if (baseUpdate !== null) {
if (last !== null) {
// For the first update, the queue is a circular linked list where
// `queue.last.next = queue.first`. Once the first update commits, and
// the `baseUpdate` is no longer empty, we can unravel the list.
last.next = null;
}
first = baseUpdate.next;
} else {
// 获取第一个 update
first = last !== null ? last.next : null;
}
if (first !== null) {
// 原来的老的值
let newState = workInProgressHook.baseState;
let newBaseState = null;
let newBaseUpdate = null;
let prevUpdate = baseUpdate;
let update = first;
let didSkip = false;
// 将 update 链循环执行完
do {
const updateExpirationTime = update.expirationTime;
// hooks 之后的expirationTime的计算大小颠倒,越大表示优先级越高
if (updateExpirationTime < renderExpirationTime) {
// 更新优先级低
// Priority is insufficient. Skip this update. If this is the first
// skipped update, the previous update/state is the new base
// update/state.
// 只有有第一个在当前更新中更新优先级小于renderExpirationTime的(需要跳过的),那么 didSkip 会被设置为 true
if (!didSkip) {
didSkip = true;
// 记录的是这个需要被跳过的 update 的前一个 update
newBaseUpdate = prevUpdate;
// 前一个 state
newBaseState = newState;
}
// Update the remaining priority in the queue.
// remainingExpirationTime 初始值为 0
// 只要有一个被跳过,则赋值为 跳过的update的对应的 expirationTime
// 最后被设置为需要跳过的 update 里优先级最大的 expirationTime
// 最后在 finishHooks 中会被设置为 fiber 对象的 expirationTime,即尚未执行更新的 update 里优先级最高的 expirationTime
if (updateExpirationTime > remainingExpirationTime) {
remainingExpirationTime = updateExpirationTime;
}
} else {
// Process this update.
const action = update.action;
// 对于 useState 来说 action 就是更新后的值,而非 function
// 即 newState = action 赋值新值
newState = reducer(newState, action);
}
prevUpdate = update;
update = update.next;
} while (update !== null && update !== first);
// 如果没有跳过的
if (!didSkip) {
// prevUpdate 为最后一个 update 即 last
newBaseUpdate = prevUpdate;
newBaseState = newState;
}
// memoizedState 更新为新的 state
workInProgressHook.memoizedState = newState;
// 更新 baseUpdate, baseUpdate 记录的要么是 last 要么是需要跳过的update 的前一个 update
// 即在开头判断 baseUpdate 如果不为 null,则从 baseUpdate.next 开始更新
workInProgressHook.baseUpdate = newBaseUpdate;
workInProgressHook.baseState = newBaseState;
}
const dispatch: Dispatch<A> = (queue.dispatch: any);
return [workInProgressHook.memoizedState, dispatch];
}
// 首次渲染, hook.queue 为空
// There's no existing queue, so this is the initial render.
if (reducer === basicStateReducer) {
// useState 的情况
// Special case for `useState`.
if (typeof initialState === 'function') {
initialState = initialState();
}
} else if (initialAction !== undefined && initialAction !== null) {
// useReducer 接受的第三个参数,用来计算 initialState
initialState = reducer(initialState, initialAction);
}
// 将 initialState 挂载到 hook 对象上的 memoizedState 及 baseState,即使得 functionComponent 也有读取 state 的能力
// hook 对象是更细粒度的 fiber
workInProgressHook.memoizedState = workInProgressHook.baseState = initialState;
// 挂载 queue,last 用来记录 dispatchAction 函数中生成的 update 链
queue = workInProgressHook.queue = {
last: null,
dispatch: null,
};
// 生成dispatch函数
const dispatch: Dispatch<A> = (queue.dispatch = (dispatchAction.bind(
null,
currentlyRenderingFiber,
queue,
): any));
return [workInProgressHook.memoizedState, dispatch];
}
即 useEffect 执行是个异步过程,要等到所有节点更新完成、浏览器重新渲染 dom 后才会执行 commitPassiveEffects。即 useLayoutEffect 中可以同步的获取更新后的 dom.
// commitRoot
//...
if (
enableHooks &&
firstEffect !== null &&
rootWithPendingPassiveEffects !== null
) {
// This commit included a passive effect. These do not need to fire until
// after the next paint. Schedule an callback to fire them in an async
// event. To ensure serial execution, the callback will be flushed early if
// we enter rootWithPendingPassiveEffects commit phase before then.
let callback = commitPassiveEffects.bind(null, root, firstEffect);
if (enableSchedulerTracing) {
// TODO: Avoid this extra callback by mutating the tracing ref directly,
// like we do at the beginning of commitRoot. I've opted not to do that
// here because that code is still in flux.
callback = Schedule_tracing_wrap(callback);
}
passiveEffectCallbackHandle = Schedule_scheduleCallback(callback);
passiveEffectCallback = callback;
}
//...
function useContext<T>(
context: ReactContext<T>,
observedBits: void | number | boolean,
): T {
// Ensure we're in a function component (class components support only the
// .unstable_read() form)
resolveCurrentlyRenderingFiber();
return readContext(context, observedBits);
}
Hoooks
定义
ReactCurrentOwner
的currentDispatcher
属性挂载hooks api
ReactCurrentOwner.currentDispatcher
是在renderRoot
的开始时时候为赋值为Dispatcher
,结束workLoop
后赋值为null
.即只有在workLoop
阶段才会有值。更新阶段调用 hooks 过程
在
beginWork
里执行updateFunctionComponent、mountIndeterminateComponent、 updateForwardRef
(即对应函数式组件)的更新,会先执行prepareToUseHooks
(和context
类似执行prepareToReadContext
), 在render
完成后,执行finishHooks
.prepareToUseHooks
finishHooks
didScheduleRenderPhaseUpdate
表示在渲染过程中产生的update
。即在functionComponent
的return
之前调用了useState
返回的setXXX
则这个变量被赋值为true
,目的是让在渲染过程中产生的update
经过while
渲染判断在一次渲染过程中就被执行掉,而不是等到下一次更新中执行hook api
都会对应一个hook
对象,挂载在functionComponent
对应的fiber
节点上的memoizedState
属性上useState
useReducer
hooks api
调用时会调用createWorkInProgressHook
创建一个hook
对象并赋值给全局变量workInProgressHook
。在
createWorkInProgressHook
内部hook
对象构建成链表结构createWorkInProgressHook
hook
对象,并构建链表结构firstWorkInProgressHook
变量在这里被赋值为第一个创建的hook
,执行完functionComponent
得到children
后会走finishHooks
,将firstWorkInProgressHook
赋值给fiber.memoizedState
,再将firstWorkInProgressHook
清空dispatchAction
dispatch
函数update
,并构建update
的链表结构,将queue.last
指向最后一个产生的update
useEffect 与 useLayoutEffect
useEffectImpl
不同的是前两个参数不同fiber
的effectTag
hook
对象的effectTag
create
为传入的匿名函数inputs
为依赖数组useLayoutEffect
会在commitRoot
的第三次循环commitAllLifeCycles
=>commitLifeCycles
中被执行,执行时机相当于componentDidMount
和componentDidUpdate
commitAllLifeCycles
时会根据nextEffect
是否有Passive (passiveEffect)
赋值rootWithPendingPassiveEffects = finishedRoot
在 执行完commitRoot
的三次循环后会判断这个变量如果存在则将commitPassiveEffects
加入到Schedule_scheduleCallback
异步任务调度中useEffect
会在commitPassiveEffects
被执行,先执行destroy
再执行create
。加入调度任务中是为了给浏览器更多时间渲染,js执行更少的时间,防止页面阻塞。useEffectImpl
fiber
标记对应的effectTag
pushEffect
创建hook
对象的effect
并标记对应的hookEffectTag
,构建effect
链赋值给fiber
对象的updateQueue
areHookInputsEqual
方法对比依赖数组,如果依赖变化则执行pushEffect
。即如果不传依赖数组,每次都会执行pushEffect
areHookInputsEqual
true
否则返回false
Polyfill
pushEffect
functionComponent
所有的effect
链,保存在fiber
对象的componentUpdateQueue
属性的lastEffect
上finishHooks
时将componentUpdateQueue
赋值给fiber.updateQueue
function createFunctionComponentUpdateQueue(): FunctionComponentUpdateQueue { return { lastEffect: null, }; }
第一次循环 commitBeforeMutationLifecycles
unmountTag -> UnmountSnapshot
。 在useEffectImpl
没有推入UnmountSnapshot。
因此不会执行destroy
mountTag -> NoHookEffect
。 也不会执行create
function commitBeforeMutationLifeCycles( current: Fiber | null, finishedWork: Fiber, ): void { switch (finishedWork.tag) { case FunctionComponent: case ForwardRef: case SimpleMemoComponent: { commitHookEffectList(UnmountSnapshot, NoHookEffect, finishedWork); return; } //... } //... }
第三次循环 commitAllLifeCycles
commitAllLifeCycles -> commitLifeCycles
unmountTag -> UnmountLayout
. 都没有标记UnmountLayout
都不会执行destroy
mountTag -> MountLayout
.useLayoutEffect
标记了MountLayout
因此 会执行它的create
方法,对应classComponent
组件会在这里执行componentDidMount
和componentDidUpdate
commitLifeCycles
之后,如果effectTag
有Passive
则赋值rootWithPendingPassiveEffects = finishedRoot
。为useEffect
执行做准备第四次执行 commitHookEffectList
commitRoot
执行完三次循环后,如果rootWithPendingPassiveEffects
不为空则将commitPassiveEffects
加入到scheduleWork
的调度队列中useEffect
执行是个异步过程,要等到所有节点更新完成、浏览器重新渲染dom
后才会执行commitPassiveEffects
。即useLayoutEffect
中可以同步的获取更新后的dom
.commitPassiveEffects
effect
找到标记了Passive
的执行commitPassiveHookEffects
commitPassiveHookEffects
unmountTag -> UnmountPassive
,mountTag -> NoHookEffect
因此只执行useEffect
的destroy
unmountTag -> NoHookEffect
, 只执行useEffect
的create
useContext
readContext
获取context
useRef
将
ref
对象挂载到hook.memoizedState
属性上。useImperativeHandle
forwardRef
往父组件传递方法或者属性useLayoutEffect
useCallback
callback
useMemo
返回的是
nextCreate
的执行结果,如果依赖项相同,则会返回上一次的执行结果