# React.js Hook 入門 - 5. Hook 的使用規則 ###### tags: `React` `Hook` `TypeScript` > Hooks are JavaScript functions, but you need to follow two rules when using them. We provide a linter plugin to enforce these rules automatically: 前面幾篇介紹了在 React 中常使用的 Hook (state, effect, context),這篇打算先提一下 Hook 的使用規則,避免錯誤的理解造成在開發上異常的發生。 ### Only Call Hooks at the Top Level 我個人的開發習慣會把 Hook 寫在 component 內的最上面,這樣在閱讀程式碼時第一眼就可以看出這個component 使用了哪些 Hook,不會因為分散在不同的段落導致漏看的情況。 Component 在 render 時,會依照順序呼叫 Hook。若是將 Hook 擺在條件判斷式、迴圈、巢狀的 function 裡面,有可能會因為邏輯的問題造成 render 的順序跟預期產生差異,導致 bug 的發生。因此你必須將 Hook 的使用寫在最上層(Top Level)。 正因為 React 是透過固定的順序 render,才有辦法在多個 useState 和 useEffect 的呼叫時都能讓你拿到正確的 state。 ### Only Call Hooks from React Functions Hook 的使用規則包含了下列兩種 1. 在 React function component 中呼叫 Hook。 2. 在自定義的 Hook 中呼叫 Hook。 藉由遵循這些規則,確保了在 component 中所有的 stateful logic 的使用情境,可以清晰地呈現在 component 跟 Hook 內。 ### Explanation ```typescript= function Form() { const [name, setName] = useState<strig>('Alice') useEffect(sayHello) const [age, setAge] = useState<number>(18) const [gender, setGender] = useState<number>('Female') function sayHello(): void { console.log(`Hi ${name}, good morning.`) } } ``` 我們可能在單一的 component 中使用多個 useState 或 useEffect,但 React 是如何知道哪個 state 要對應到哪個 useState 的呼叫?答案是 React 仰賴於 Hook 被呼叫的順序。我們的範例能執行是因為在每一次的 render 中 Hook 都是依照一樣的順序被呼叫。 ```typescript= // ------------ // 第一次 render // ------------ useState('Alice') // 1. 用 'Mary' 來初始化 name state 變數 useEffect(sayHello) // 2. 增加一個 effect useEffect(18) // 3. 用 18 來初始化 age state 變數 useEffect('Female') // 4. 用 'Female' 來初始化 gender state 變數 // ------------- // 第二次 render // ------------- useState('Alice') // 1. 讀取 name state 變數 (參數被忽略了) useEffect(sayHello) // 2. 替換了原本 sayHello 的 effect useEffect(18) // 3. 讀取 age state 變數 (參數被忽略了) useState('Female') // 4. 讀取 gender state 變數 (參數被忽略了) ``` 假如範例改成這樣 ```typescript= function Form() { const [name, setName] = useState<strig>('Alice') if(name !== '') { useEffect(sayHello) } const [age, setAge] = useState<number>(18) const [gender, setGender] = useState<number>('Female') function sayHello(): void { console.log(`Hi ${name}, good morning.`) } } ``` 假如發生了 ```name === ''``` 的情況發生,會導致此次 render 時 useEffect 沒有被執行到的情況,造成 Hook 呼叫順序不同(之後的 Hook 都往前 shift 了一個),此時 bug 就產生了。 ```typescript= useState('Alice') // 1. 讀取 name state 變數 (參數被忽略了) // useEffect(sayHello) // 此次這個 Hook 沒有執行到 useEffect(18) // 2.(原本是 3) useState('Female') // 3.(原本是 4) ``` 前面提到將 Hook 的使用寫在最上層(Top Level),若你需要使用到條件判斷式,可以使用下列的方式,即可避免 Hook 沒有被執行到的情況發生。 ```typescript= function Form() { const [name, setName] = useState<strig>('Alice') useEffect(sayHello) const [age, setAge] = useState<number>(18) const [gender, setGender] = useState<number>('Female') function sayHello(): void { if(name !== '') { console.log(`Hi ${name}, good morning.`) } } } ``` ### 小結 Hook 的使用看似簡單容易上手,但小細節沒有注意到所產生的 bug 可能會讓你沒有發覺也不知如何解決。官方有提供了 lint 的規則,讓你可以優雅的避免掉這些問題,但了解這些規則背後的意義能讓你在將來的開發上更得心應手。 ##### Reference [Rules of Hooks](https://reactjs.org/docs/hooks-rules.html) [eslint-plugin-react-hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks)