---
# System prepended metadata

title: React.js Hook 入門 - 5. Hook 的使用規則
tags: [React, TypeScript, Hook]

---

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