在上一章中,我们学习了如何使用 React 社区开发的各种 Hook,以及在哪里可以找到更多。我们学习了用 Hook 替换 React 生命周期方法,使用工具和数据管理 Hook,使用 Hook 进行响应式设计,以及使用 Hook 实现撤消/重做功能。最后,我们学习了在哪里可以找到其他 Hook。
在本章中,我们将学习有关使用 Hook 的所有知识,以及使用和开发我们自己的 Hook 时需要注意的事项。 Hook 对它们的调用顺序有一定的限制。违反 Hook 的规则可能会导致错误或意外行为,因此我们需要确保我们学习并执行这些规则。
本章将介绍以下主题:
- 调用 Hook
- Hook 的顺序
- Hook 的名称
- 执行 Hook 的规则
- 处理
useEffect
依赖关系
技术要求
应该已经安装了相当新版本的 Node.js(vll.l2.0 或更高版本)。Node.js 的 npm
包管理器也需要安装。
本章的代码可以在 GitHub 存储库中找到:https://github.com/PacktPublishing/Learn-React-Hooks/tree/master/Chapter09。
观看以下视频,了解代码的实际应用:
请注意,强烈建议您自己编写代码。不要简单地运行已提供的代码示例。为了正确学习和理解,自己编写代码很重要。但是,如果遇到任何问题,始终可以参考代码示例。
现在,让我们从本章开始。
调用 Hook
Hook 应该只在 React 函数组件或自定义 Hook中调用。它们不能在类组件或常规 JavaScript 函数中使用。
可以在以下级别的顶层调用 Hook :
- React 函数组件
- 自定义 Hook(我们将在下一章中学习如何创建自定义 Hook)
正如我们所看到的,Hook 大多是普通的 JavaScript 函数,除了它们依赖于在 React 函数组件中定义。当然,使用其他 Hook 的自定义 Hook 可以在 React 函数组件之外定义,但是在使用 Hook 时,我们始终需要确保在 React 函数组件中调用它们。接下来,我们将了解有关 Hook 顺序的规则。
Hook 的顺序
仅在函数组件或自定义 Hook 的顶层开头调用 Hook。
不要在条件、循环或嵌套函数中调用 Hook——这样做会改变 Hook 的顺序,从而导致错误。我们已经了解到,更改 Hook 的顺序会导致状态在多个 Hook 之间混淆。
在第 2 章使用 State Hook中,我们了解到不能执行以下操作:
const [ enableFirstName, setEnableFirstName ] = useState(false)
const [ name, setName ] = enableFirstName
? useState('')
: [ '', () => {} ]
const [ lastName, setLastName ] = useState('')
我们为 firstName
和 lastName
渲染了一个复选框和两个输入字段,然后在 lastName
字段中输入了一些文本:
重温第 2 章使用 State Hook 中的示例
目前,Hook 的顺序如下:
enableFirstName
lastName
接下来,我们单击复选框以启用 firstName
字段。这样做改变了 Hook 的顺序,因为现在我们的 Hook 定义如下所示:
enableFirstName
firstName
lastName
由于 React 完全依靠 Hook 的顺序来管理它们的状态,所以 firstName
字段现在是第二个 Hook,所以它从 lastName
字段中获取状态:
从第 2 章使用 State Hook 改变 Hook 顺序的问题
如果我们在示例 2 中使用来自 React 的真正 useState
Hook “我们可以定义条件 Hook 吗”?从 第 2 章 “使用 State Hook”中,我们可以看到 React 会自动检测 Hook 的顺序何时发生变化,并且会显示警告:
React 在检测到 Hook 的顺序已更改时打印警告
在开发模式下运行 React 时,当渲染比之前的渲染更多的 Hook 时,它还会崩溃并显示未捕获的不变违规错误消息:
当 Hook 数量发生变化时,在开发模式下 React 崩溃
正如我们所看到的,更改 Hook 的顺序或有条件地启用 Hook 是不可能的,因为 React 内部使用 Hook 的顺序来跟踪哪些数据属于哪个 Hook。
Hook 的名称
有一个约定,Hook 函数应始终以 use
为前缀,后跟以大写字母开头的 Hook 名称;例如: useState
、 useEffect
和 useResource
。这很重要,因为不这样我们将不知道哪些 JavaScript 函数是 Hook,哪些不是。特别是在执行 Hook 的规则时,我们需要知道哪些函数是 Hook,这样我们才能确保它们不会被有条件地或循环调用。
正如我们所看到的,命名约定在技术上不是必需的,但它们使开发人员的生活变得更加轻松。了解普通函数和 Hook 之间的区别使得自动执行 Hook 的规则变得非常容易。在下一节中,我们将学习如何使用 eslint
工具自动执行规则。
执行 Hook 的规则
如果我们坚持在 Hook 函数前面加上 use
的约定,我们可以自动执行其他两个规则:
- 仅从 React 函数组件或自定义 Hook 调用 Hook
- 仅在顶层调用 Hook(不在循环、条件或嵌套函数内部)
为了自动执行规则,React 提供了一个名为 eslint-plugin-react-hooks
的 eslint
插件,它会自动检测何时使用 Hook,并确保规则不被破坏。ESLint 是一个 linter,它是一种分析源代码并发现诸如风格错误、潜在错误和编程错误等问题的工具。
将来,create-react-app 将默认包含此插件。
设置 eslint-plugin-react-hooks
我们现在将设置 React Hook eslint
插件来自动执行 Hook 的规则。
让我们开始安装和启用 eslint
插件:
- 首先,我们必须通过
npm
安装插件:
npm install --save-dev eslint-plugin-react-hooks
我们在这里使用 --save-dev 标志,因为在部署应用程序时不需要安装 eslint 及其插件。我们只需要在开发应用程序期间使用它们。
- 然后,我们在项目文件夹的根目录中创建一个新的
.eslintrc.json
文件,其中包含以下内容。我们从react-app
ESLint 配置开始扩展:
{
"extends": "react-app",
- 接下来,包括我们之前安装的
react-hooks
插件:
"plugins": [
"react-hooks"
],
- 现在我们启用两个规则。首先,我们告诉
eslint
在我们违反rules-of-hooks
规则时显示错误。此外,我们启用exhaustive-deps
规则作为警告:
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}
- 最后,我们调整
package.json
来定义一个新的lint
脚本,它将调用eslint
:
"scripts": {
"lint": "npx eslint src/",
现在,我们可以执行 npm run lint
,我们将看到有 5 个警告和 0 个错误:
使用 react-hooks 插件执行 ESLint
我们现在将尝试打破 Hook 的规则;例如,通过编辑 src/user/Login.js
并使第二个输入 Hook 成为条件:
const { value: password, bindToinput: bindPassword } = loginFailed ?
useInput('') : [ '', () => {} ]
当我们再次执行 npm run lint
时,我们可以看到现在有一个错误:
在打破 Hook 规则后执行 ESLint
正如我们所看到的,eslint
通过迫使我们遵守 Hook 的规则来帮助我们。当我们违反任何规则时,linter 会抛出错误,并在 Effect Hook 缺少依赖项时显示警告。听 eslint
将帮助我们避免错误和意外行为,因此我们永远不应该忽视它的错误或警告。
示例代码
示例代码可以在 Chapter09/chapter9_1
文件夹中找到。
只需运行 npm install
即可安装所有依赖项并执行 npm run lint
来运行 linter。
处理 useEffect 依赖关系
除了强制执行 Hook 的规则之外,我们还在检查 Effect Hook 中使用的所有变量是否都传递给了它的依赖数组。这种详尽的依赖关系规则可确保每当 Effect Hook 中使用的内容(函数、值等)发生变化时,Hook 将再次触发。
正如我们在上一节中看到的,在使用 npm run lint
运行 linter 时,有几个与穷举依赖关系规则相关的警告。通常,它与 dispatch
函数或其他不属于依赖数组的函数有关。通常,这些功能不应该改变,但我们永远无法确定,所以最好将它们添加到依赖项中。
使用 eslint 自动修复警告
由于详尽的依赖关系规则非常简单明了,我们可以自动让 eslint
修复它。
为此,我们需要将 --fix
标志传递给 eslint
。使用 npm run
,我们可以通过使用额外的 --
作为分隔符来传递标志,如下所示:
npm run lint --fix
运行上述命令后,我们可以再次运行 npm run lint
,我们将看到所有警告都已自动修复:
让 ESLint 修复它们后没有警告
正如我们所看到的,eslint
不仅可以警告我们问题,甚至可以自动为我们修复其中一些问题!
示例代码
示例代码可以在 Chapter09/chapter9_2
文件夹中找到。
只需运行 npm install
即可安装所有依赖项并执行 npm run lint
来运行linter。
总结
在本章中,我们首先了解了 Hook 的两个规则:我们应该只从 React 函数组件调用 Hook,并且我们需要确保 Hook 的顺序保持不变。此外,我们了解了 Hook 的命名约定,它们应该始终以 use
前缀开头。然后,我们学习了如何使用 eslint
执行 Hook 的规则。最后,我们了解了 useEffect
依赖项,以及如何使用 eslint
自动修复缺少的依赖项。
了解 Hook 的规则并执行它们对于避免错误和意外行为非常重要。在创建我们自己的 Hook 时,这些规则尤其重要。现在我们已经很好地掌握了 Hook 的工作原理,包括它们的规则和约定,在下一章中,我们将学习如何创建自己的 Hook!
问题
为了回顾我们在本章中学到的内容,请尝试回答以下问题:
- Hook 可以在哪里调用?
- 我们可以在 React 类组件中使用 Hook 吗?
- 关于 Hook 的顺序,我们需要注意什么?
- 可以在条件、循环或嵌套函数中调用 Hook 吗?
- Hook 的命名约定是什么?
- 我们如何自动执行 Hook 的规则?
- 什么是穷举依赖关系规则?
- 我们如何自动修复 linter 警告?
延伸阅读
如果您对我们在本章中学到的概念的更多信息感兴趣,请查看以下阅读材料:
- React 官方文档中的 Hook 规则:https://reactjs.org/docs/hooks-rules.html。
- ESLint 官方网站:https://eslint.org/.
评论 (0)