hook
hook调用入口
在hook
源码中hook
存在于Dispatcher
中,Dispatcher
就是一个对象,不同hook
调用的函数不一样,全局变量ReactCurrentDispatcher.current
会根据是mount
还是update
赋值为HooksDispatcherOnMount
或HooksDispatcherOnUpdate
1 2 3 4
| ReactCurrentDispatcher.current = current === null || current.memoizedState === null ? HooksDispatcherOnMount : HooksDispatcherOnUpdate;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| const HooksDispatcherOnMount: Dispatcher = { useCallback: mountCallback, useContext: readContext, useEffect: mountEffect, useImperativeHandle: mountImperativeHandle, useLayoutEffect: mountLayoutEffect, useMemo: mountMemo, useReducer: mountReducer, useRef: mountRef, useState: mountState, };
const HooksDispatcherOnUpdate: Dispatcher = { useCallback: updateCallback, useContext: readContext, useEffect: updateEffect, useImperativeHandle: updateImperativeHandle, useLayoutEffect: updateLayoutEffect, useMemo: updateMemo, useReducer: updateReducer, useRef: updateRef, useState: updateState, };
|
hook数据结构
在FunctionComponent
中,多个hook
会形成hook
链表,保存在Fiber
的memoizedState
的上,而需要更新的Update
保存在hook.queue.pending
中
1 2 3 4 5 6 7
| const hook: Hook = { memoizedState: null, baseState: null, baseQueue: null, queue: null, next: null, };
|
下面来看下memoizedState
对应的值
useState
:例如const [state, updateState] = useState(initialState)
,memoizedState
等于state
的值
useReducer
:例如const [state, dispatch] = useReducer(reducer, {})
,``memoizedState等于
state`的值
useEffect
:在mountEffect
时会调用pushEffect
创建effect
链表,memoizedState
就等于effect
链表,effect
链表也会挂载到fiber.updateQueue
上,每个effect
上存在useEffect
的第一个参数回调和第二个参数依赖数组,例如,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
函数,useMemo
保存callback
的执行结果
useState&useReducer
之所以把useState
和useReducer
放在一起,是因为在源码中useState
就是有默认reducer
参数的useReducer
。
声明
`resolveDispatcher`函数会获取当前的`Dispatcher`
1 2 3 4 5 6 7 8
| 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
调用mountState
,useReducer
调用mountReducer
,唯一区别就是它们创建的queue
中lastRenderedReducer
不一样,mount有初始值basicStateReducer
,所以说useState
就是有默认reducer
参数的useReducer
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| function mountState<S>( initialState: (() => S) | S, ): [S, Dispatch<BasicStateAction<S>>] { const hook = mountWorkInProgressHook(); if (typeof initialState === 'function') { initialState = initialState(); } hook.memoizedState = hook.baseState = initialState; const queue = (hook.queue = { pending: null, dispatch: null, lastRenderedReducer: basicStateReducer, lastRenderedState: (initialState: any), }); const dispatch: Dispatch< BasicStateAction<S>, > = (queue.dispatch = (dispatchAction.bind( null, currentlyRenderingFiber, quewque, ): any)); return [hook.memoizedState, dispatch]; }
function mountReducer<S, I, A>( reducer: (S, A) => S, initialArg: I, init?: I => S, ): [S, Dispatch<A>] { const hook = mountWorkInProgressHook(); let initialState; if (init !== undefined) { initialState = init(initialArg); } else { initialState = ((initialArg: any): S); } hook.memoizedState = hook.baseState = initialState; const queue = (hook.queue = { pending: null, dispatch: null, lastRenderedReducer: reducer, lastRenderedState: (initialState: any), }); const dispatch: Dispatch<A> = (queue.dispatch = (dispatchAction.bind( null, currentlyRenderingFiber, queue, ): any)); return [hook.memoizedState, dispatch]; }
|
1 2 3
| function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S { return typeof action === 'function' ? action(state) : action; }
|
update阶段
update
时会根据hook
中的update
计算新的state
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function updateReducer<S, I, A>( reducer: (S, A) => S, initialArg: I, init?: I => S, ): [S, Dispatch<A>] { const hook = updateWorkInProgressHook(); const queue = hook.queue; queue.lastRenderedReducer = reducer;
const dispatch: Dispatch<A> = (queue.dispatch: any); return [hook.memoizedState, dispatch]; }
|
执行阶段
useState
执行setState
后会调用dispatchAction
,dispatchAction
做的事情就是将Update
加入queue.pending
中,然后开始调度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| function dispatchAction(fiber, queue, action) {
var update = { eventTime: eventTime, lane: lane, suspenseConfig: suspenseConfig, action: action, eagerReducer: null, eagerState: null, next: null };
var alternate = fiber.alternate;
if (fiber === currentlyRenderingFiber$1 || alternate !== null && alternate === currentlyRenderingFiber$1) { } didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate = true; } else { if (fiber.lanes === NoLanes && (alternate === null || alternate.lanes === NoLanes)) { }
scheduleUpdateOnFiber(fiber, lane, eventTime); } }
|
useEffect
声明
获取并返回useEffect
函数
1 2 3 4 5 6 7
| export function useEffect( create: () => (() => void) | void, deps: Array<mixed> | void | null, ): void { const dispatcher = resolveDispatcher(); return dispatcher.useEffect(create, deps); }
|
mount阶段
调用mountEffect
,mountEffect
调用mountEffectImpl
,hook.memoizedState
赋值为effect
链表
1 2 3 4 5 6 7 8 9 10 11
| function mountEffectImpl(fiberFlags, hookFlags, create, deps): void { const hook = mountWorkInProgressHook(); const nextDeps = deps === undefined ? null : deps; currentlyRenderingFiber.flags |= fiberFlags; hook.memoizedState = pushEffect( HookHasEffect | hookFlags, create, undefined, nextDeps, ); }
|
update阶段
浅比较依赖,如果依赖性变了pushEffect
第一个参数传HookHasEffect | hookFlags
,HookHasEffect
表示useEffect
依赖项改变了,需要在commit阶段重新执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 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)) { pushEffect(hookFlags, create, destroy, nextDeps); return; } } }
currentlyRenderingFiber.flags |= fiberFlags;
hook.memoizedState = pushEffect( HookHasEffect | hookFlags, create, destroy, nextDeps, ); }
|
执行阶段
在commit阶段的commitLayoutEffects
函数中会调用schedulePassiveEffects
,将useEffect
的销毁和回调函数push到pendingPassiveHookEffectsUnmount
和pendingPassiveHookEffectsMount
中,然后在mutation之后调用flushPassiveEffects
依次执行上次render的销毁函数回调和本次render的回调函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| 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(); } 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; effect.destroy = create(); } catch (error) { captureCommitPhaseError(fiber, error); } }
|
useRef
string
类型的ref
已经不在推荐使用,ForwardRef
只是把ref
通过传参传下去,createRef
也是{current: any}
这种结构,所以我们只讨论function
或者{current: any}
的useRef
1 2 3 4 5 6 7 8 9
| export function createRef(): RefObject { const refObject = { current: null, }; return refObject; }
let children = Component(props, secondArg);
|
声明阶段
和其他hook一样
1 2 3 4
| export function useRef<T>(initialValue: T): {|current: T|} { const dispatcher = resolveDispatcher(); return dispatcher.useRef(initialValue); }
|
mount阶段
mount时会调用mountRef
,创建hook和ref对象。
1 2 3 4 5
| function mountRef<T>(initialValue: T): {|current: T|} { const hook = mountWorkInProgressHook(); const ref = {current: initialValue}; hook.memoizedState = ref; return ref;
|
render阶段
将带有ref属性的Fiber
标记上Ref Tag
,在一步发生在beginWork
和completeWork
函数中的markRef
1
| export const Ref = 0b0000000010000000;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function markRef(current: Fiber | null, workInProgress: Fiber) { const ref = workInProgress.ref; if ( (current === null && ref !== null) || (current !== null && current.ref !== ref) ) { workInProgress.effectTag |= Ref; } }
function markRef(workInProgress: Fiber) { workInProgress.effectTag |= Ref; }
|
commit阶段
会在commitMutationEffects
函数中判断ref
是否改变,如果改变了会先执行commitDetachRef
先删除之前的ref
,然后在commitLayoutEffect
中会执行commitAttachRef
赋值ref
。
1 2 3 4 5 6 7 8 9 10 11 12
| function commitMutationEffects(root: FiberRoot, renderPriorityLevel) { while (nextEffect !== null) { const effectTag = nextEffect.effectTag;
if (effectTag & Ref) { const current = nextEffect.alternate; if (current !== null) { commitDetachRef(current); } } }
|
1 2 3 4 5 6 7 8 9 10 11
| function commitDetachRef(current: Fiber) { const currentRef = current.ref; if (currentRef !== null) { if (typeof currentRef === 'function') { currentRef(null); } else { currentRef.current = null; } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function commitAttachRef(finishedWork: Fiber) { const ref = finishedWork.ref; if (ref !== null) { const instance = finishedWork.stateNode; let instanceToUse; switch (finishedWork.tag) { case HostComponent: instanceToUse = getPublicInstance(instance); break; default: instanceToUse = instance; }
if (typeof ref === 'function') { ref(instanceToUse); } else { ref.current = instanceToUse; } } }
|
update阶段
update时调用updateRef
获取获取当前useRef
,然后返回hook链表
1 2 3 4
| function updateRef<T>(initialValue: T): {|current: T|} { const hook = updateWorkInProgressHook(); return hook.memoizedState; }
|
useMemo&useCallback
声明阶段
和其他hook 一样
mount阶段
mount阶段useMemo
和useCallback
唯一区别是在memoizedState
中存贮callback
还是callback计算出来的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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; }
function mountCallback<T>(callback: T, deps: Array<mixed> | void | null): T { const hook = mountWorkInProgressHook(); const nextDeps = deps === undefined ? null : deps; hook.memoizedState = [callback, nextDeps]; return callback; }
|
update阶段
update时也一样,唯一区别就是直接用回调函数
还是执行回调后返回的value
作为[?, nextDeps]
赋值给memoizedState
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| 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) { 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; }
function updateCallback<T>(callback: T, deps: Array<mixed> | void | null): T { const hook = updateWorkInProgressHook(); 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]; } } }
hook.memoizedState = [callback, nextDeps]; return callback; }
|