Hooks

React Hook 是一种复用状态逻辑的方式。

在 Hooks 出现之前,开发 React 组件主要是类组件和函数组件。函数组件没有 state,所以也叫 SFC(stateless functional component),简单的将 props 映射成 view;Class 组件有 state,能够处理更加复杂的逻辑。

但是类组件并非完美,主要有以下三个主要的问题:

  • 代码复用:在组件之间复用状态逻辑很难

React 没有提供将可复用性行为附加到组件的途径。解决这类问题的现行方案是使用高阶组件和 render props。但是这类解决方案需要重新组织你的组件结构,providersconsumers、高阶组件和 render props 等其他抽象层组成的组件也会形成组件的嵌套地狱。

React 需要为 共享状态逻辑 提供更好的原生途径。使用 Hooks 可从组件中提取状态逻辑,使得这些逻辑可单独测试并复用。Hooks 使你在无需修改组件结构的情况下复用状态逻辑。

  • 复杂组件变得难以理解

维护的组件充斥着副作用,每个生命周期中包含一些不相关的逻辑,例如获取数据、事件监听等。它们相互关联且需要对照修改的代码进行拆分,而完全不相关的代码却在同一个方法中组合起来。容易产生 BUG,并且导致逻辑不一致。

不能拆分更小的粒度,因为状态逻辑无处不在,给测试带来一些挑战。这也是很多应用引入状态管理库的原因,但这同时也会带来很多抽象的概念,需要在不同文件间切换,使得复用变得更难。

Hooks 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据),而非强制按照生命周期划分。还可以使用 reducer 来管理组件的内部状态,使其更加可预测。

  • 难以理解的 Class

Class 是学习 React 的一大屏障,需要理解 this 的工作方式,不能忘记绑定事件处理器,没有稳定的语法提案,这些代码非常冗余。

Class 组件会无意中鼓励开发者使用一些让优化措施无效的方案。也给目前工具带来问题,例如 Class 不能很好地压缩,并且会使热重载出现不稳定的情况。

Hook 使你在非 Class 情况下使用更多的 React 特性。

概念上,React 组件一直更像是函数。而 Hook 则拥抱了函数,同时没有牺牲 React 的精神原则。Hook 提供了问题的解决方案,无需学习复杂的函数式或响应式编程技术。

其实,React Hooks 带来的好处不仅是更函数式、更新颗粒度更细、代码更清晰,还有以下三个优点:

  1. 多个状态不会产生嵌套,写法还是平铺的:如 async/await 之于 callback hell 一样,Hooks 也解决了高阶组件的嵌套地狱问题。虽然 render props 也可以通过 compose 解决这个问题,但使用略为繁琐,而且因为强制封装一个新对象而增加了实体数量。
  2. Hooks 可以引用其他 Hooks,自定义 Hooks 更加灵活。
  3. 更容易将组件的 UI 与状态分离。

原则

  • 只能在函数组件中调用:不要在普通的 JavaScript 函数中调用 Hook
  • 只能在函数组件的最顶层作用域使用:不要在循环、条件或嵌套函数中调用 Hook

自定义 Hook

通过自定义 Hook,可以将组件逻辑提取到可重用的函数中。

当我们想在两个函数之间共享逻辑时,我们会把它提取到第三个函数中。而组件和 Hook 都是函数,所以也同样适用这种方式。

自定义 Hook 必须以 use 开头吗?

必须如此。这个约定非常重要。不遵循的话,由于无法判断某个函数是否包含对其内部 Hook 的调用,React 将无法自动检查你的 Hook 是否违反了 Hook 规则。

在两个组件中使用相同的 Hook 会共享 state 吗?

不会。自定义 Hook 是一种 重用状态逻辑的机制(例如设置为订阅并存储当前值),所以每次使用自定义 Hook 时,其中的所有 state 和副作用都是完全隔离的。

自定义 Hook 如何获取独立的 state

每次调用 Hook,它都会获取独立的 state。由于我们直接调用 useFriedStatus,从 React 的角度来看,我们的组件只是调用了 useStateuseEffect。正如我们在之前章节中了解到的一样,我们可以在一个组件中多次调用 useStateuseEffect,它们是完全独立的。