iT邦幫忙

2021 iThome 鐵人賽

DAY 23
0
Modern Web

用30天更加認識 React.js 這個好朋友系列 第 23

Day23-React Life Cycle 篇-下篇(Updating & Unmounting & Error handling & Render Phase & Commit Phase)

這篇繼續介紹生命週期的另外兩個階段: Updating & Unmounting,以及了解元件在各個生命週期中更新 DOM 的兩個階段 Render Phase & Commit Phase。

Updating

Updating 階段總共有六個,以下詳細介紹:

第一階段: getDerivedStateFromProps()

請見上篇介紹。

第二階段: shouldComponentUpdate()

shouldComponentUpdate(newProps, newState) 它接收了兩個參數,分別是新的 props 和 state,而這個函式的作用可以從名字就知道,可以用來控制是不是要讓元件做更新,可以用它來做效能優化。

這個函式最後回傳的是一個 boolean 值,如果是 true 就進入到 render 函式,false 就不會往以下的 life cycle 階段進行(不會更新元件)
。若沒實作 shouldComponentUpdate() 預設會返回 true。

另外還要注意 shouldComponentUpdate() 它在更新 props 和 state 時會呼叫,但如果是透過 forceUpdate() 更新元件就不會呼叫 shouldComponentUpdate()。

第三階段: render()

請見上篇介紹。

第四階段: getSnapshotBeforeUpdate()

getSnapshotBeforeUpdate(prevProps, prevState)這個函式在元件實際渲染前被呼叫,接收的兩個參數分別是修改前的 props 和 state,用來取得元件 DOM 被修改前的一些資訊。

getSnapshotBeforeUpdate 被執行後的回傳值會被傳進 componentDidUpdate 的第三個參數。

這個函式不常使用。

第五階段: 更新 DOM 元素和 Refs

第六階段: componentDidUpdate()

元件剛剛更新完畢時呼叫,componentDidUpdate(prevProps, prevState, snapshot)總共可以傳入三個參數,分別是更新前的 props、state 和 getSnapshotBeforeUpdate 的回傳值。

它可以用在元件更新後對應的後續動作,例如比對新props/state 和舊 props/state 的差異,存取 DOM、呼叫 api 等。

若要在 componentDidUpdate() 內去使用 setState,記得要加上條件判斷,否則一直更新 state 一直導致元件重新渲染會造成無窮迴圈。

Unmounting

最後是元件移除的階段,不過也就只有一個函式-componentWillUnmount()。

componentWillUnmount()

這個元件作用是將一些用不到的 request、event listeners、setTimeout 移除,等於 useEffect cleanup 的作用。

範例:

componentWillUnmount() {
  clearInterval(this.interval);
}

Error handling

在 React life cycle 裡也有內建的錯誤處理函式,有錯誤發生時可以使用它們。

getDerivedStateFromError()

static getDerivedStateFromError(error)這個函式為一個靜態方法,主要是用來更新 state,讓錯誤畫面出現。

記得不能在這裡處理 side effect,並且只能回傳更新的 state 或 null。

static getDerivedStateFromError(error) {
  return { hasError: true };
}

componentDidCatch()

componentDidCatch(error, info)這個函式可以捕捉從子元件中拋出的錯誤,並將這個錯誤訊息提供給 Error Boundary 元件。

這個函式就可以處理 side effect。

Error Boundary

中文可以翻譯成"錯誤邊界",這個名詞的概念是說如果某個元件發生錯誤時,包覆該元件的 Error Boundary 元件可以把錯誤訊息呈現在網頁上,並避免影響其它的父元件。

Error Boundary 的限制:

  • 只能捕捉子元件的錯誤,Error Boundary 元件本身不能捕捉自己
  • 只能捕捉從 constructor(), render() 和各 Lifecycle Methods 中發生的錯誤
  • Event Handler & 非同步 (Asynchronous) & Server Side Render 程式中發生的錯誤無法被捕捉

所以還是要用 try catch 來處理無法被 Error Boundary 捕捉的錯誤。

範例:

<ErrorBoundary>
  <OtherComponent />
</ErrorBoundary>
class ErrorBoundary extends Component {
  state = { errorMessage: null };

  static getDerivedStateFromError(error) {
    // update state
    return { errorMessage: error.message };
  }

  componentDidCatch(error, info) {
    console.log(error, info);
  }

  render() {
    if (this.state.errorMessage) {
      return <h1>{this.state.errorMessage}</h1>;
    }
    return this.props.children;
  }
}

最後介紹的是圖片中左側的 Render Phase、Commit Phase,詳細說的話兩個階段中間還有一個 Pre-Commit Phase。

Render Phase

React 在此階段時會透過 render 函式將 JSX 做轉換並生成新的 Virtual DOM ,然後和舊的 Real DOM 做比較(Diff),決定出哪些部分要實際更新到 DOM 元素上。

限制

這個階段不能有 side effect,並且可以被暫停、中斷、重新開始,因此這個階段包含的建立 constructor、shouldComponentUpdate、render 等函式都必須是純粹函式。

Pre-Commit Phase

這個階段發生在 React 要把 React element 加到 DOM 的前一刻,可以讀取 DOM。

Commit Phase

將要更新的 Virtual DOM 更新到實際的 DOM 元素,可以進行 side effect 的操作。


參考資料

React lifecycle methods: An approachable tutorial with examples

React 元件生命週期 (Component Lifecycle)

React 錯誤邊界元件

I Want To Know React - Lifecycle 階段


上一篇
Day22-React Life Cycle 篇-上篇(介紹生命週期圖 & Mounting)
下一篇
Day24-React 效能優化篇-上篇(四個優化效能的技巧)
系列文
用30天更加認識 React.js 這個好朋友32

尚未有邦友留言

立即登入留言