超级面板
文章目录
最新文章
最近更新
文章分类
标签列表
文章归档

React Hooks 文档翻译 - 5 - Rules of Hooks(Hooks 的使用规则)

翻译自:https://reactjs.org/docs/hooks-rules.html

Hooks 是 React 16.8 新增的功能。它允许你在不编写类的情况下使用状态和其他 React 特性。

Hooks 是 JavaScript 函数,但在使用时需要遵循两个规则。我们提供了一个linter 插件 来自动检查这些规则:

Only Call Hooks at the Top Level

只在顶层调用 Hooks

不要在循环语句,条件语句或嵌套函数中调用 Hook。而是在 React 函数的顶层使用 Hooks。遵循此规则,可以确保组件每次渲染时都以相同的顺序调用 Hook。这是 React 能够在多次的 useStateuseEffect 调用后正确保留 Hooks 状态的原因。(如果你很好奇这是为什么,我们将在后面深入解释。)

Only Call Hooks from React Functions

只在 React 函数组件中调用 Hooks

不要在普通的 JavaScript 函数中调用 Hooks。 相反,你可以:

  • 在 React 函数组件中调用 Hooks。
  • 在自定义的 Hooks 中调用 Hooks(我们将在下一页了解它们)。

遵循这两条规则,可以确保组件中的所有的状态逻辑在源码中很清晰。

ESLint Plugin

我们发布了一个名为 eslint-plugin-react-hooks 的 ESLint 插件,来强制执行这两个规则。如果你想尝试,可以添加到项目中:

npm install eslint-plugin-react-hooks@next
// Your ESLint configuration
{
"plugins": [
// ...
"react-hooks"
],
"rules": {
// ...
"react-hooks/rules-of-hooks": "error"
}
}

将来,我们打算将此插件包含在 Create React App 和类似的脚手架中。

你可以直接跳到下一页,查看如何编写自己的 Hooks 我们将在本文继续解释这些规则背后的原因。

Explanation

前文所述,我们可以在一个组件中使用多个 State 或 Effect Hook:

function Form() {
// 1. Use the name state variable
const [name, setName] = useState('Mary');

// 2. Use an effect for persisting the form
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});

// 3. Use the surname state variable
const [surname, setSurname] = useState('Poppins');

// 4. Use an effect for updating the title
useEffect(function updateTitle() {
document.title = name + ' ' + surname;
});

// ...
}

那么 React 如何知道哪个状态对应于哪个 useState 的调用?答案是** React 依赖于调用 Hooks 的顺序。 **我们的示例之所以可以工作,因为 Hooks 被调用的顺序在每次渲染都是相同的:

// ------------
// First render
// ------------
useState('Mary') // 1. Initialize the name state variable with 'Mary'
useEffect(persistForm) // 2. Add an effect for persisting the form
useState('Poppins') // 3. Initialize the surname state variable with 'Poppins'
useEffect(updateTitle) // 4. Add an effect for updating the title

// -------------
// Second render
// -------------
useState('Mary') // 1. Read the name state variable (argument is ignored)
useEffect(persistForm) // 2. Replace the effect for persisting the form
useState('Poppins') // 3. Read the surname state variable (argument is ignored)
useEffect(updateTitle) // 4. Replace the effect for updating the title

// ...

只要 Hooks 调用的顺序在多次渲染时都是相同的,React 就可以将其本地状态与它们关联起来。但是如果我们在条件语句中调用 Hook (例如,persistForm effect)会发生什么?

// 🔴 We're breaking the first rule by using a Hook in a condition
if (name !== '') {
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
}

条件 name !== '' 在第一次渲染时为 true,因此运行此 Hook。但是,在下一次渲染时,用户可能会清除表单,使条件值为 false 。此时的渲染过程中将跳过此 Hook,Hooks 被调用的顺序将变得和之前不同:

useState('Mary')           // 1. Read the name state variable (argument is ignored)
// useEffect(persistForm) // 🔴 This Hook was skipped!
useState('Poppins') // 🔴 2 (but was 3). Fail to read the surname state variable
useEffect(updateTitle) // 🔴 3 (but was 4). Fail to replace the effect

React 不知道第二个 useState Hook调用返回什么。React 期望此组件中的第二个 Hook 对应 persistForm effect,就像上次渲染时一样,但它已不再存在。此时,在我们跳过的 Hook 之后的每个 Hook 调用也被移动,结果导致错误。

这就是必须在我们组件的顶层调用 Hooks 的原因。如果想要在条件语句下运行一个 effect,可以把这个条件放在 Hook 中:

useEffect(function persistForm() {
// 👍 We're not breaking the first rule anymore
if (name !== '') {
localStorage.setItem('formData', name);
}
});

请注意,如果使用了提供的 lint 规则,则无需担心此问题。 不过现在你知道了为什么 Hooks 以这种方式工作,以及这些规则可以阻止了哪些问题。

Next Steps

Finally, we’re ready to learn about writing your own Hooks! Custom Hooks let you combine Hooks provided by React into your own abstractions, and reuse common stateful logic between different components.

最后,我们已经准备好去了解编写自己的Hooks!Custom Hooks 允许你将 React 提供的 Hooks 组合到你的抽象中,并在不同组件之间重用常见的状态逻辑。