書接上文數組
effect
也就是在React中咱們常說的side effect,在React中相似像componentDidMount這樣的生命週期方法中,由於可能會執行setState這樣的方法而產生新的更新,咱們稱爲side effect即替代。自己FunctionalComponent由於是pure function,因此不會產生任何的異常,而useEffect和useLayoutEffect則是帶給產生FunctionalComponent交替能力的掛鉤,他們的行爲很是相似componentDidMount和componentDidUpdatebash
function createFunctionComponentUpdateQueue(): FunctionComponentUpdateQueue {
return {
lastEffect: null,
};
}
function pushEffect(tag, create, destroy, inputs) {
const effect: Effect = {
tag,
create,
destroy,
inputs,
// Circular
next: (null: any),
};
if (componentUpdateQueue === null) {
componentUpdateQueue = createFunctionComponentUpdateQueue();
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;
}
複製代碼
useLayoutEffect
UpdateEffect
useEffect
UpdateEffect | PassiveEffect
useLayoutEffect
UnmountMutation | MountLayout
useEffect
UnmountPassive | MountPassive
areHookInputsEqual
NoHookEffect
在如下三個階段都會調用commitHookEffectList
方法,咱們來看一下:異步
commitWork
中commitHookEffectList(UnmountMutation, MountMutation, finishedWork);
commitBeforeMutationLifeCycles
中commitHookEffectList(UnmountSnapshot, NoHookEffect, finishedWork);
commitLifeCycles
中commitHookEffectList(UnmountLayout, MountLayout, finishedWork);
commitHookEffectList
這個方法的內容就是根據傳入的unmountTag
和mountTag
來判斷是否須要執行對應的destory
和create
方法,這是在每一個Hook
對象的effect
鏈上的。因此看這部分代碼最重要的其實就是看他傳入的effectTag
和Hook對象上的effectTag
的對比。ide
對比結果就是:ui
useLayoutEffect
的destory
會在commitWork
的時候被執行;而他的create
會在commitLifeCycles
的時候被執行。useEffect
在這個流程中都不會被執行。能夠看出來useLayoutEffect
的執行過程跟componentDidMount
和componentDidUpdate
很是類似,因此React官方也說了,若是你必定要選擇一個相似於生命週期方法的Hook,那麼useLayoutEffect
是不會錯的那個,可是咱們推薦你使用useEffect
,在你清除他們的區別的前提下,後者是更好的選擇。spa
那麼useEffect
何時被調用呢?code
答案在commitRoot
的最後,他等其餘sideEffect
所有commit
完了以後,會執行如下代碼:component
if (
enableHooks &&
firstEffect !== null &&
rootWithPendingPassiveEffects !== null
) {
let callback = commitPassiveEffects.bind(null, root, firstEffect);
passiveEffectCallbackHandle = Schedule_scheduleCallback(callback);
passiveEffectCallback = callback;
}複製代碼
rootWithPendingPassiveEffects
是在commitAllLifeCycles
的時候若是發現更新中有passive effect
的節點的話,就等於FiberRoot
。orm
if (enableHooks && effectTag & Passive) {
rootWithPendingPassiveEffects = finishedRoot;
}複製代碼
這裏若是有,則會發起一次Schedule_scheduleCallback
,這個就是咱們以前講的異步調度模塊Scheduler
的方法,流程跟PerformWork
相似,這裏咱們再也不重複講解。對象
但咱們看到這裏就清楚了,useEffect
的destory
和create
都是異步調用的,因此他們不會影響本次更新的提交,因此不會由於在effect
中產生了新的更新而致使阻塞DOM渲染的狀況。
那麼commitPassiveEffects
作了啥呢?
export function commitPassiveHookEffects(finishedWork: Fiber): void {
commitHookEffectList(UnmountPassive, NoHookEffect, finishedWork);
commitHookEffectList(NoHookEffect, MountPassive, finishedWork);
}複製代碼
正好對應了useEffect
設置的sideEffect
。