认识
一、认识
为了能让函数组件可以保存一些状态,执行一些副作用钩子,React Hooks
应运而生,它可以帮助记录 React
中组件的状态,处理一些额外的副作用。
二、规则
Hooks
使用规则:
-
只能在函数内部的最外层调用
Hook
,不要在循环、条件判断或者子函数中调用 -
只能在
React
的函数组件中调用Hook
,不要在其他JavaScript
函数中调用 -
命令规范
-
useState
返回数组的第二项以set
开头(仅作为约定) -
自定义
Hooks
以use
开头(可被 lint 校验)
-
三、问题
3.1 为什么不可以在函数组件外部使用 Hooks ?
在 Mount
阶段, Render BeginWork
递阶段, 从根 Fiber
开始, 对每个 Fiber
节点,调用对应的处理函数。处理逻辑如下(主要以函数组件为例): 遇到函数组件, 调用 renderWithHooks
执行函数组件。在执行过程中, React
按照 Hook
调用的顺序遍历组件内部所有 Hook
, 在 Mount
阶段的每一个 Hook
会调用 MountHookXX
来为每一个 Hook
创建一个 Hook
对象, 存储在 当前处理的 fiber.memoizedState
中(注意, 如果 Hook
不在函数组件中调用, 此时获取不到当前正在处理的 Fiber
, 无法进行后续流程) 。
在 Update
阶段, Render BeginWork
递阶段, 从根 Fiber
开始, 对每个 Fiber
节点,调用对应的处理函数。处理逻辑如下(主要以函数组件为例): 遇到函数组件, 调用 renderWithHooks
执行函数组件。在执行过程中, 从 当前处理的 Fiber.memoizedState
中获取 Hook
链表。(注意, 如果 Hook
不在函数组件中调用, 此时获取不到当前正在处理的 Fiber
, 无法进行后续流程) 。
3.2 为什么不可以在条件语句、循环条件里面使用 Hooks ?
在 Mount
阶段, Render BeginWork
递阶段, 从根 Fiber
开始, 对每个 Fiber
节点,调用对应的处理函数。处理逻辑如下(主要以函数组件为例): 遇到函数组件, 调用 renderWithHooks
执行函数组件。在执行过程中, React
按照 Hook
调用的顺序遍历组件内部所有 Hook
, 在 Mount
阶段的每一个 Hook
会调用 MountHookXX
来为每一个 Hook
创建一个 Hook
对象, 存储在 当前处理的 fiber.memoizedState
中。此时, Hooks
链表中的 Hook
顺序与函数组件中的 Hook
调用顺序一致。
在 Update
阶段, Render BeginWork
递阶段, 从根 Fiber
开始, 对每个 Fiber
节点,调用对应的处理函数。处理逻辑如下(主要以函数组件为例): 遇到函数组件, 调用 renderWithHooks
执行函数组件。在执行过程中, 从 当前处理的 Fiber.memoizedState
中获取 Hook
链表, 依次遍历 Hooks
链表。
因此, 必须保证 mount
阶段 和 update
阶段 阶段的 hook
执行顺序一致。否则, 有可能在其中一次的渲染中, 因为一个 hook
没有执行, currentHook.next
与 将要执行的 hook
明显是不对应的,造成代码错误。
结论: 凡是使用 currentHook
、 workInProgressHook
来获取 hook
的 hook
, 都必须放在顶部, 绝对不可以在条件和循环里使用, 必须保证 hooks
在每一次渲染中都按照同样的顺序被调用。
3.3 Vue 组合函数与 React Hooks 的区别?
React Hooks
在 Render BeginWork
递阶段, 遇到函数组件 Fiber
, 执行函数组件, 在执行过程中, React
按照 Hook
调用的顺序遍历组件内部所有 Hook
。也就是说, React Hooks
在组件每次更新时都会重新调用。并且, 由于 React Hooks
的实现方式, Mount
阶段和 Update
中的 Hooks
是不同的逻辑。因此, Hooks
有严格的调用顺序,并不可以写在条件分支中。因为, 有可能在其中一次的渲染中, 因为一个 hook
没有执行, currentHook.next
与 将要执行的 hook
明显是不对应的, 造成代码错误。 其中, 基于依赖项的 Hooks
具有闭包问题, 需要传入正确的依赖数组, 或者需要通过 useRef
来保持状态。
Vue
组合函数 仅会在 setup()
或者 <script setup>
中调用一次, 组合式 API
也并不限制调用顺序,还可以有条件地进行调用。针对依赖项的 Hooks
, Vue
的响应性系统运行时会自动收集计算属性和侦听器的依赖,因此无需手动声明依赖, 不会存在闭包问题。
因此, Vue
组合函数 具有后发优势, 基于 React Hooks
的设计理念, 结合 Vue
自身响应式机制, 实现了一种更好用的 Hooks
。
3.4 Vue 3.0 Hooks 与 React Hooks 区别?
React Hooks
在 Render BeginWork
递阶段, 遇到函数组件 Fiber
, 执行函数组件, 在执行过程中, React
按照 Hook
调用的顺序遍历组件内部所有 Hook
。也就是说, React Hooks
在组件每次更新时都会重新调用。并且, 由于 React Hooks
的实现方式, Mount
阶段和 Update
中的 Hooks
是不同的逻辑。因此, Hooks
有严格的调用顺序,并不可以写在条件分支中。因为, 有可能在其中一次的渲染中, 因为一个 hook
没有执行, currentHook.next
与 将要执行的 hook
明显是不对应的, 造成代码错误。 其中, 基于依赖项的 Hooks
具有闭包问题, 需要传入正确的依赖数组, 或者需要通过 useRef
来保持状态。
Vue
组合函数 仅会在 setup()
或者 <script setup>
中调用一次, 组合式 API
也并不限制调用顺序,还可以有条件地进行调用。针对依赖项的 Hooks
, Vue
的响应性系统运行时会自动收集计算属性和侦听器的依赖,因此无需手动声明依赖, 不会存在闭包问题。
因此, Vue
组合函数 具有后发优势, 基于 React Hooks
的设计理念, 结合 Vue
自身响应式机制, 实现了一种更好用的 Hooks
。