鉤子是JavaScript函數,但在使用它們時須要遵循兩個規則。咱們提供了一個 linter 插件來自動執行這些規則react
不要在循環、條件和嵌套的函數中調用Hook。npm
遵循此規則能夠確保每次組件render時都以相同的順序調用Hook。這是React能在多個useState和useEffect之間正確保存Hook的state緣由。 (下面將深刻解釋。)bash
不要在普通的JS函數中調用Hook函數
使用 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依賴於調用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組合到您本身的抽象中,並重用不一樣組件之間的常見有狀態邏輯