# 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)