人人都能讀懂的react源碼解析(大廠高薪必備)
9.hooks源碼(想知道Function Component是怎樣保存狀態(tài)的嘛)
視頻課程&調(diào)試demos
視頻課程的目的是為了快速掌握react源碼運(yùn)行的過程和react中的scheduler叶撒、reconciler补疑、renderer、fiber等阿浓,并且詳細(xì)debug源碼和分析丙躏,過程更清晰墨叛。
視頻課程:進(jìn)入課程
demos:demo
課程結(jié)構(gòu):
- 開篇(聽說你還在艱難的啃react源碼)
- react心智模型(來來來,讓大腦有react思維吧)
- Fiber(我是在內(nèi)存中的dom)
- 從legacy或concurrent開始(從入口開始,然后讓我們奔向未來)
- state更新流程(setState里到底發(fā)生了什么)
- render階段(厲害了,我有創(chuàng)建Fiber的技能)
- commit階段(聽說renderer幫我們打好標(biāo)記了,映射真實節(jié)點吧)
- diff算法(媽媽再也不擔(dān)心我的diff面試了)
- hooks源碼(想知道Function Component是怎樣保存狀態(tài)的嘛)
- scheduler&lane模型(來看看任務(wù)是暫停、繼續(xù)和插隊的)
- concurrent mode(并發(fā)模式是什么樣的)
- 手寫迷你react(短小精悍就是我)
hook調(diào)用入口
在hook源碼中hook存在于Dispatcher中骏全,Dispatcher就是一個對象与斤,不同hook 調(diào)用的函數(shù)不一樣肪康,全局變量ReactCurrentDispatcher.current會根據(jù)是mount還是update賦值為HooksDispatcherOnMount或HooksDispatcherOnUpdate
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null//mount or update
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
const HooksDispatcherOnMount: Dispatcher = {//mount時
useCallback: mountCallback,
useContext: readContext,
useEffect: mountEffect,
useImperativeHandle: mountImperativeHandle,
useLayoutEffect: mountLayoutEffect,
useMemo: mountMemo,
useReducer: mountReducer,
useRef: mountRef,
useState: mountState,
//...
};
const HooksDispatcherOnUpdate: Dispatcher = {//update時
useCallback: updateCallback,
useContext: readContext,
useEffect: updateEffect,
useImperativeHandle: updateImperativeHandle,
useLayoutEffect: updateLayoutEffect,
useMemo: updateMemo,
useReducer: updateReducer,
useRef: updateRef,
useState: updateState,
//...
};
hook數(shù)據(jù)結(jié)構(gòu)
在FunctionComponent中,多個hook會形成hook鏈表撩穿,保存在Fiber的memoizedState的上磷支,而需要更新的Update保存在hook.queue.pending中
const hook: Hook = {
memoizedState: null,//對于不同hook,有不同的值
baseState: null,//初始state
baseQueue: null,//初始queue隊列
queue: null,//需要更新的update
next: null,//下一個hook
};
下面來看下memoizedState對應(yīng)的值
- useState:例如
const [state, updateState] = useState(initialState)
食寡,memoizedState等于
state的值 - useReducer:例如
const [state, dispatch] = useReducer(reducer, {});
雾狈,memoizedState等于
state的值 - useEffect:在mountEffect時會調(diào)用pushEffect創(chuàng)建effect鏈表,
memoizedState
就等于effect鏈表抵皱,effect鏈表也會掛載到fiber.updateQueue上善榛,每個effect上存在useEffect的第一個參數(shù)回調(diào)和第二個參數(shù)依賴數(shù)組,例如呻畸,useEffect(callback, [dep])
移盆,effect就是{create:callback, dep:dep,...} - useRef:例如
useRef(0)
,memoizedState就等于
{current: 0} - useMemo:例如
useMemo(callback, [dep])
伤为,memoizedState
等于[callback(), dep]
- useCallback:例如
useCallback(callback, [dep])
咒循,memoizedState
等于[callback, dep]
。useCallback
保存callback
函數(shù)绞愚,useMemo
保存callback
的執(zhí)行結(jié)果
useState&useReducer
之所以把useState和useReducer放在一起叙甸,是因為在源碼中useState就是有默認(rèn)reducer參數(shù)的useReducer。
-
useState&useReducer聲明
resolveDispatcher函數(shù)會獲取當(dāng)前的Dispatcher
function useState(initialState) { var dispatcher = resolveDispatcher(); return dispatcher.useState(initialState); } function useReducer(reducer, initialArg, init) { var dispatcher = resolveDispatcher(); return dispatcher.useReducer(reducer, initialArg, init); }
-
mount階段
mount階段useState調(diào)用mountState位衩,useReducer調(diào)用mountReducer裆蒸,唯一區(qū)別就是它們創(chuàng)建的queue中l(wèi)astRenderedReducer不一樣,mount有初始值basicStateReducer糖驴,所以說useState就是有默認(rèn)reducer參數(shù)的useReducer僚祷。
function mountState<S>(// initialState: (() => S) | S, ): [S, Dispatch<BasicStateAction<S>>] { const hook = mountWorkInProgressHook();//創(chuàng)建當(dāng)前hook if (typeof initialState === 'function') { initialState = initialState(); } hook.memoizedState = hook.baseState = initialState;//hook.memoizedState賦值 const queue = (hook.queue = {//賦值hook.queue pending: null, dispatch: null, lastRenderedReducer: basicStateReducer,//和mountReducer的區(qū)別 lastRenderedState: (initialState: any), }); const dispatch: Dispatch<//創(chuàng)建dispatch函數(shù) BasicStateAction<S>, > = (queue.dispatch = (dispatchAction.bind( null, currentlyRenderingFiber, quewque, ): any)); return [hook.memoizedState, dispatch];//返回memoizedState和dispatch } function mountReducer<S, I, A>( reducer: (S, A) => S, initialArg: I, init?: I => S, ): [S, Dispatch<A>] { const hook = mountWorkInProgressHook();//創(chuàng)建當(dāng)前hook let initialState; if (init !== undefined) { initialState = init(initialArg); } else { initialState = ((initialArg: any): S); } hook.memoizedState = hook.baseState = initialState;//hook.memoizedState賦值 const queue = (hook.queue = {//創(chuàng)建queue pending: null, dispatch: null, lastRenderedReducer: reducer, lastRenderedState: (initialState: any), }); const dispatch: Dispatch<A> = (queue.dispatch = (dispatchAction.bind(//創(chuàng)建dispatch函數(shù) null, currentlyRenderingFiber, queue, ): any)); return [hook.memoizedState, dispatch];//返回memoizedState和dispatch }
function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S { return typeof action === 'function' ? action(state) : action; }
-
update階段
update時會根據(jù)hook中的update計算新的state
function updateReducer<S, I, A>( reducer: (S, A) => S, initialArg: I, init?: I => S, ): [S, Dispatch<A>] { const hook = updateWorkInProgressHook();//獲取hook const queue = hook.queue; queue.lastRenderedReducer = reducer; //...更新state和第5章的state計算邏輯基本一致 const dispatch: Dispatch<A> = (queue.dispatch: any); return [hook.memoizedState, dispatch]; }
-
執(zhí)行階段
useState執(zhí)行setState后會調(diào)用dispatchAction,dispatchAction做的事情就是講Update加入queue.pending中贮缕,然后開始調(diào)度
function dispatchAction(fiber, queue, action) { var update = {//創(chuàng)建update eventTime: eventTime, lane: lane, suspenseConfig: suspenseConfig, action: action, eagerReducer: null, eagerState: null, next: null }; //queue.pending中加入update var alternate = fiber.alternate; if (fiber === currentlyRenderingFiber$1 || alternate !== null && alternate === currentlyRenderingFiber$1) { //如果是render階段執(zhí)行的更新didScheduleRenderPhaseUpdate=true } didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate = true; } else { if (fiber.lanes === NoLanes && (alternate === null || alternate.lanes === NoLanes)) { //如果fiber不存在優(yōu)先級并且當(dāng)前alternate不存在或者沒有優(yōu)先級久妆,那就不需要更新了 //優(yōu)化的步驟 } scheduleUpdateOnFiber(fiber, lane, eventTime); } }
useEffect
-
聲明
獲取并返回useEffect函數(shù)
export function useEffect( create: () => (() => void) | void, deps: Array<mixed> | void | null, ): void { const dispatcher = resolveDispatcher(); return dispatcher.useEffect(create, deps); }
-
mount階段
調(diào)用mountEffect,mountEffect調(diào)用mountEffectImpl跷睦,hook.memoizedState賦值為effect鏈表
function mountEffectImpl(fiberFlags, hookFlags, create, deps): void { const hook = mountWorkInProgressHook();//獲取hook const nextDeps = deps === undefined ? null : deps;//依賴 currentlyRenderingFiber.flags |= fiberFlags;//增加flag hook.memoizedState = pushEffect(//memoizedState=effects環(huán)狀鏈表 HookHasEffect | hookFlags, create, undefined, nextDeps, ); }
-
update階段
淺比較依賴,如果依賴性變了pushEffect第一個參數(shù)傳HookHasEffect | hookFlags肋演,HookHasEffect表示useEffect依賴項改變了抑诸,需要在commit階段重新執(zhí)行
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)) {//比較deps //即使依賴相等也要將effect加入鏈表烂琴,以保證順序一致 pushEffect(hookFlags, create, destroy, nextDeps); return; } } } currentlyRenderingFiber.flags |= fiberFlags; hook.memoizedState = pushEffect( //參數(shù)傳HookHasEffect | hookFlags,包含hookFlags的useEffect會在commit階段執(zhí)行這個effect HookHasEffect | hookFlags, create, destroy, nextDeps, ); }
-
執(zhí)行階段
在第9章commit階段的commitLayoutEffects函數(shù)中會調(diào)用schedulePassiveEffects蜕乡,將useEffect的銷毀和回調(diào)函數(shù)push到pendingPassiveHookEffectsUnmount和pendingPassiveHookEffectsMount中奸绷,然后在mutation之后調(diào)用flushPassiveEffects依次執(zhí)行上次render的銷毀函數(shù)回調(diào)和本次render 的回調(diào)函數(shù)
const unmountEffects = pendingPassiveHookEffectsUnmount; pendingPassiveHookEffectsUnmount = []; for (let i = 0; i < unmountEffects.length; i += 2) { const effect = ((unmountEffects[i]: any): HookEffect); const fiber = ((unmountEffects[i + 1]: any): Fiber); const destroy = effect.destroy; effect.destroy = undefined; if (typeof destroy === 'function') { try { destroy();//銷毀函數(shù)執(zhí)行 } catch (error) { captureCommitPhaseError(fiber, error); } } } const mountEffects = pendingPassiveHookEffectsMount; pendingPassiveHookEffectsMount = []; for (let i = 0; i < mountEffects.length; i += 2) { const effect = ((mountEffects[i]: any): HookEffect); const fiber = ((mountEffects[i + 1]: any): Fiber); try { const create = effect.create;//本次render的創(chuàng)建函數(shù) effect.destroy = create(); } catch (error) { captureCommitPhaseError(fiber, error); } }
useRef
sring類型的ref已經(jīng)不在推薦使用,F(xiàn)orwardRef只是把ref通過傳參傳下去层玲,createRef也是{current: any這種結(jié)構(gòu)号醉,所以我們只討論function或者{current: any}的useRef
//createRef返回{current: any}
export function createRef(): RefObject {
const refObject = {
current: null,
};
return refObject;
}
//ForwardRef第二個參數(shù)是ref對象
let children = Component(props, secondArg);
-
聲明階段
和其他hook一樣
export function useRef<T>(initialValue: T): {|current: T|} { const dispatcher = resolveDispatcher(); return dispatcher.useRef(initialValue); }
-
mount階段
mount時會調(diào)用mountRef,創(chuàng)建hook和ref對象辛块。
function mountRef<T>(initialValue: T): {|current: T|} { const hook = mountWorkInProgressHook();//獲取useRef const ref = {current: initialValue};//ref初始化 hook.memoizedState = ref; return ref; }
render階段:將帶有ref屬性的Fiber標(biāo)記上Ref Tag畔派,在一步發(fā)生在beginWork和completeWork函數(shù)中的markRef
export const Ref = /* */ 0b0000000010000000;
//beginWork中 function markRef(current: Fiber | null, workInProgress: Fiber) { const ref = workInProgress.ref; if ( (current === null && ref !== null) || (current !== null && current.ref !== ref) ) { workInProgress.effectTag |= Ref; } } //completeWork中 function markRef(workInProgress: Fiber) { workInProgress.effectTag |= Ref; }
commit階段:
會在commitMutationEffects函數(shù)中判斷ref是否改變,如果改變了會先執(zhí)行commitDetachRef先刪除之前的ref润绵,然后在commitLayoutEffect中會執(zhí)行commitAttachRef賦值ref线椰。
function commitMutationEffects(root: FiberRoot, renderPriorityLevel) { while (nextEffect !== null) { const effectTag = nextEffect.effectTag; // ... if (effectTag & Ref) { const current = nextEffect.alternate; if (current !== null) { commitDetachRef(current);//移除ref } } }
function commitDetachRef(current: Fiber) { const currentRef = current.ref; if (currentRef !== null) { if (typeof currentRef === 'function') { currentRef(null);//類型是function,則調(diào)用 } else { currentRef.current = null;//否則賦值{current: null} } } }
function commitAttachRef(finishedWork: Fiber) { const ref = finishedWork.ref; if (ref !== null) { const instance = finishedWork.stateNode;//獲取ref的實例 let instanceToUse; switch (finishedWork.tag) { case HostComponent: instanceToUse = getPublicInstance(instance); break; default: instanceToUse = instance; } if (typeof ref === 'function') {//ref賦值 ref(instanceToUse); } else { ref.current = instanceToUse; } } }
-
update階段
update時調(diào)用updateRef獲取獲取當(dāng)前useRef尘盼,然后返回hook鏈表
function updateRef<T>(initialValue: T): {|current: T|} { const hook = updateWorkInProgressHook();//獲取當(dāng)前useRef return hook.memoizedState;//返回hook鏈表 }
useMemo&useCallback
-
聲明階段
和其他hook 一樣
-
mount階段
mount階段useMemo和useCallback唯一區(qū)別是在memoizedState中存貯callback還是callback計算出來的函數(shù)
function mountMemo<T>( nextCreate: () => T, deps: Array<mixed> | void | null, ): T { const hook = mountWorkInProgressHook();//創(chuàng)建hook const nextDeps = deps === undefined ? null : deps; const nextValue = nextCreate();//計算value hook.memoizedState = [nextValue, nextDeps];//把value和依賴保存在memoizedState中 return nextValue; } function mountCallback<T>(callback: T, deps: Array<mixed> | void | null): T { const hook = mountWorkInProgressHook();//創(chuàng)建hook const nextDeps = deps === undefined ? null : deps; hook.memoizedState = [callback, nextDeps];//把callback和依賴保存在memoizedState中 return callback; }
-
update階段
update時也一樣憨愉,唯一區(qū)別就是直接用回調(diào)函數(shù)還是執(zhí)行回調(diào)后返回的value作為[?, nextDeps]賦值給memoizedState
function updateMemo<T>(
nextCreate: () => T,
deps: Array<mixed> | void | null,
): T {
const hook = updateWorkInProgressHook();//獲取hook
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
if (nextDeps !== null) {
const prevDeps: Array<mixed> | null = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {//淺比較依賴
return prevState[0];//沒變 返回之前的狀態(tài)
}
}
}
const nextValue = nextCreate();//有變化重新調(diào)用callback
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
function updateCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
const hook = updateWorkInProgressHook();//獲取hook
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
if (nextDeps !== null) {
const prevDeps: Array<mixed> | null = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {//淺比較依賴
return prevState[0];//沒變 返回之前的狀態(tài)
}
}
}
hook.memoizedState = [callback, nextDeps];//變了重新將[callback, nextDeps]賦值給memoizedState
return callback;
}