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 属性
版权声明: 本文为 InfoQ 作者【全栈潇晨】的原创文章。
原文链接:【http://xie.infoq.cn/article/45e3d73a5f50e18af90557052】。文章转载请联系作者。












 
    
评论