react 源码解析 13.hooks 源码
react 源码解析 13.hooks 源码
视频讲解(高效学习):进入学习
往期文章:
hook 调用入口
在 hook 源码中 hook 存在于 Dispatcher 中,Dispatcher 就是一个对象,不同 hook 调用的函数不一样,全局变量 ReactCurrentDispatcher.current 会根据是 mount 还是 update 赋值为 HooksDispatcherOnMount 或 HooksDispatcherOnUpdate
hook 数据结构
在 FunctionComponent 中,多个 hook 会形成 hook 链表,保存在 Fiber 的 memoizedState 的上,而需要更新的 Update 保存在 hook.queue.pending 中
下面来看下 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。
useState&useReducer 声明
resolveDispatcher 函数会获取当前的 Dispatcher
mount 阶段
mount 阶段 useState 调用 mountState,useReducer 调用 mountReducer,唯一区别就是它们创建的 queue 中 lastRenderedReducer 不一样,mount 有初始值 basicStateReducer,所以说 useState 就是有默认 reducer 参数的 useReducer。
update 阶段
update 时会根据 hook 中的 update 计算新的 state
执行阶段
useState 执行 setState 后会调用 dispatchAction,dispatchAction 做的事情就是讲 Update 加入 queue.pending 中,然后开始调度
useEffect
声明
获取并返回 useEffect 函数
mount 阶段
调用 mountEffect,mountEffect 调用 mountEffectImpl,hook.memoizedState 赋值为 effect 链表
update 阶段
浅比较依赖,如果依赖性变了 pushEffect 第一个参数传 HookHasEffect | hookFlags,HookHasEffect 表示 useEffect 依赖项改变了,需要在 commit 阶段重新执行
执行阶段
在第 9 章 commit 阶段的 commitLayoutEffects 函数中会调用 schedulePassiveEffects,将 useEffect 的销毁和回调函数 push 到 pendingPassiveHookEffectsUnmount 和 pendingPassiveHookEffectsMount 中,然后在 mutation 之后调用 flushPassiveEffects 依次执行上次 render 的销毁函数回调和本次 render 的回调函数
useRef
sring 类型的 ref 已经不在推荐使用(源码中 string 会生成 refs,发生在 coerceRef 函数中),ForwardRef 只是把 ref 通过传参传下去,createRef 也是{current: any 这种结构,所以我们只讨论 function 或者{current: any}的 useRef
声明阶段
和其他 hook 一样
mount 阶段
mount 时会调用 mountRef,创建 hook 和 ref 对象。
render 阶段:将带有 ref 属性的 Fiber 标记上 Ref Tag,这一步发生在 beginWork 和 completeWork 函数中的 markRef
commit 阶段:
会在 commitMutationEffects 函数中判断 ref 是否改变,如果改变了会先执行 commitDetachRef 先删除之前的 ref,然后在 commitLayoutEffect 中会执行 commitAttachRef 赋值 ref。
update 阶段
update 时调用 updateRef 获取获取当前 useRef,然后返回 hook 链表
useMemo&useCallback
声明阶段
和其他 hook 一样
mount 阶段
mount 阶段 useMemo 和 useCallback 唯一区别是在 memoizedState 中存贮 callback 还是 callback 计算出来的函数
update 阶段
update 时也一样,唯一区别就是直接用回调函数还是执行回调后返回的 value 作为[?, nextDeps]赋值给 memoizedState
useLayoutEffect
useLayoutEffect 和 useEffect 一样,只是调用的时机不同,它是在 commit 阶段的 commitLayout 函数中同步执行
forwardRef
forwardRef 也非常简单,就是传递 ref 属性
评论