[React Hooks 翻譯] 5-8 Hook規則

鉤子是JavaScript函數,但在使用它們時須要遵循兩個規則。咱們提供了一個 linter 插件來自動執行這些規則react

只在最頂層調用Hook

不要在循環、條件和嵌套的函數中調用Hook。npm

遵循此規則能夠確保每次組件render時都以相同的順序調用Hook。這是React能在多個useState和useEffect之間正確保存Hook的state緣由。 (下面將深刻解釋。)bash

只在React函數和自定義Hook中調用Hook

不要在普通的JS函數中調用Hook函數

  1. 只在React函數中調用Hook
  2. 只在自定義Hook中調用Hook

ESLinit 插件

使用 eslint-plugin-react-hooks 確保上面2條規則工具

npm install eslint-plugin-react-hooks --save-dev
複製代碼
// Your ESLint configuration
{
  "plugins": [
    // ...
    "react-hooks"
  ],
  "rules": {
    // ...
    "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
    "react-hooks/exhaustive-deps": "warn" // Checks effect dependencies
  }
}
複製代碼

未來,咱們打算默認將此插件包含在Create React App和相似的工具包中。ui

解釋

如前所述,咱們能夠在單個組件中使用多個State或Effect Hookspa

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的順序。Hook調用的順序在每次渲染時都是相同的插件

// ------------
// 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

// ...
複製代碼

只要Hook調用的順序在渲染之間是相同的,React就能夠將一些local state與Hook一一對應。可是若是咱們把Hook放在條件語句中(例如,persistForm效果)會發生什麼呢?eslint

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

第一次渲染時name !== ' '可能 是true,下一次渲染時可能用戶更改了表單,致使name !== ' '是false,那麼此次render將會跳過Hook的執行code

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效果,就像在前一個render時同樣,但useEffect(persistForm)不存在。此後後面的一個Hook都和前一次對不上號了,而後引起錯誤。

**這就是必須在咱們組件的頂層調用Hooks的緣由。**若是咱們想要有條件地執行一個effect,咱們應該把這個條件放在Hook之中:

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

若是使用提供的lint規則,則無需擔憂此問題。可是如今你也知道爲何Hooks以這種方式工做

下一篇

咱們已經準備好了解如何編寫本身的Hooks! Custom Hooks容許您將React提供的Hook組合到您本身的抽象中,並重用不一樣組件之間的常見有狀態邏輯

相關文章
相關標籤/搜索