---
# System prepended metadata

title: React 渲染機制 與 生命週期

---

# React 渲染機制 與 生命週期

![](https://i.imgur.com/FgYUSGa.png)



人有生老病死，元件也有三大生命週期

- Mounting : 元件的 instance 剛開始被建立 ( 一個 instance 只會有一次)
- Updating: 元件內部的 props 或 state 被更新的時候
- Unmounting: 元件被移除的時候

而每個生命週期各別的過程當中，又分兩大階段

Render 階段 
- 沒有 effect ( 例如修改實際 DOM、呼叫 API ) 這個階段是 react 把「施工藍圖」畫出來，來決定
- 把 **Virtual DOM snapshot** 畫出來，並且和先前的 Virtual DOM snapshot 比較，來決定後續要更新哪些**差異部分**，其中又稱為 diff 計算
- render 的重點在於 : **決定、擬定**哪些內容要實際 render 到 DOM 上
Commit 階段
- 按照 render 階段產生的「施工藍圖」，實際對 DOM 進行操作  ( Mounting & Updating )


## Hook 時代的 flow 與 cleanup function
![](https://i.imgur.com/h405LRL.png)


在 Class 時期，每個生命週期會有許多對應的 method 來處理，我認為到了 Hooks 時代還是有生命週期，但是每個週期底下 Hooks 已經整合許多過程

其中圖中的 `Effects` 便對應到 `useEffect`

若 component 有設定 `useEffect`，每一次瀏覽器繪製完成後便開始執行 ( 實際是否執行 就要看程式碼內容跟 dependencies array )

比較容易讓人摸不著頭緒的是 cleanup function，**可以藉由回傳一個函數，來清除上一次的 effect** 

為什麼需要 cleanup function ? 如果如果想在 `useEffect` 設立倒數計時，每次 render 完成執行 effect，就開始一個新的 `setTimeout`，如果 render多次**可能會有超多個計時器同時在進行**

所以每次一有新的倒數計時器開始時，需要去清除上一個計時器，這種情況很適合用在 `debouncing`

如果 `useEffect`有 **回傳 cleanup function**，執行的時機點有兩個 : 
1. Updating 階段，瀏覽器繪製完畢，**執行上次 effect 回傳的 cleanup function**
2. Unmounting 階段，**執行上次 effect 回傳的 cleanup function**


```jsx
  const [textValue, setTextValue] = useState("");

  useEffect(() => {
    const id = setTimeout(() => {
      console.log("Send validation request");
    }, 2000);

    return () => {
      clearTimeout(id);
      console.log("clear settimeout");
    };
  }, [textValue]);
```

第一次 render 的 effect
- 設立一個新的 `setTimeout` 假設 id =1
- 回傳一個 cleanup function 函數，內容是 **「清除 id = 1 的 計時器」**

第二次 render 的 effect
- 執行上次 effect 回傳的 cleanup function，**「清除 id = 1 的 計時器」**
- 設立一個新的 `setTimeout` 假設 id = 2
- 回傳一個函數，內容是 「清除 id = 2 的 計時器」


## 實作 debouncing

[可以到codesandbox試試看](https://codesandbox.io/s/debouncing-dmupvx?file=/src/App.js:154-454)


## useEffect 大致對應的 Class 生命週期

`useEffect` 透過很巧妙的方式，來類比到 Class 生命週期中的 `componentDidMount`、`componentWillUnmount`和`componentDidUpdate`，因為這三個 method 都是在 render 完畢後執行

但是想要用 `useEffect`完全一模一樣實現這些 Class method，就不太必要了，畢竟 Hook 思維不能和 Class 完全對應 

另外 useEffect 的 cleanup function 不像 `componentWillUnmount`只有元件移除的時候才執行

另外，若有 dependencies array，也不能完全類比到 `componentDidUpdate`，因為 dependencies 的情況下，第一次 render 還是會去執行 effect，re-render 的時候才去透過 dependencies 是否改變，來決定是否觸發 useEffect


## Reference


https://ithelp.ithome.com.tw/articles/10244959

https://ithelp.ithome.com.tw/articles/10223344