iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 6
2
Modern Web

React 30天系列 第 6

Day 06-生命有限好好把握(Lifecycle)

前情提要:
昨天說明了class component和state的操作使用,也認識了contructor和super,我們講到可以在生命週期的期間攔截component的行為,今天就來講講生命週期(lifecycle)是由哪些方法(method)組成的吧。

每個人的生命都有出生到死亡
https://media.giphy.com/media/3o6Zt0CsF7OvEtNtao/giphy.gif

component(組件)也有掛載卸載(移除)
我們先來看看component的生命週期圖,以下為常用流程:
https://ithelp.ithome.com.tw/upload/images/20181013/20111595f1us7MocoA.png

從區塊來看,我們可以看到灰底的部分分成三大區塊,分別為Mounting(掛載)Updating(更新)Unmounting(卸載)

Mounting(掛載)

在這個階段,我們創建了component,準備把寫好的React element插入到真實的DOM裡時,會歷經:

constructor()

一般來說,我們用到constructor只有兩個目的:

  • 初始化state
  • 綁定event handler到instance(實例)

render()

class components唯一必備method。
當render被調用時,他會檢查this.props和this.state並返回以下類型的其中一項:

  • React elements - 我們用JSX創建的react element,指引react要渲染DOM(<div/>)或是我們定義的其他component(<MyComponent/>)。
  • Arrays and fragments - 讓我們一次可以返回多個react element (詳情請洽fragments)。
  • Portals - 這個用來渲染children到DOM subtree(詳情請洽portals)。
  • String and numbers - 這個用來渲染文字節點(Text node)到DOM上。
  • Booleans or null - 什麼都不渲染。用法會像是return test && <Child />,其中的test就是boolean。

componentDidMount()

componentDidMount會在mounted(掛載完)後馬上被調用,就是我們順利把react element掛到DOM上後會來執行componentDidMount。
以下列出會需要使用componentDidMount的兩種使用情境:

  • 需透過網路請求(network requests)取得資料(如:透過ajax取資料)
  • 設定訂閱(subscriptions)來偵聽行為的好地方(如:透過event listener監聽resizescroll)。如果有設定subscriptions,記得要在componentWillUnmount取消訂閱(unsubscribe)。

備註:此階段使用setState

Updating(更新)

以上就是component掛載的大略過程,掛載完成後當component收到新的propssetStateforceUpdate時會重新render一次component。

componentDidUpdate(prevProps, prevState, snapshot)

再次完成render後會執行componentDidUpdate,在此貼心的react會傳給你更新前的props(prevProps)、更新前的state(prevState)和snapshot(另一個少用的lifecycle,從getSnapshotBeforeUpdate捎來的訊息)
使用情境:

  • 同上,如果有網路請求(network requests)需處理可在此動手,唯一要注意的就是可以先比對更新前和更新後的props,先做條件判斷是否需要發送request取得資料,如以下程式碼所示:
componentDidUpdate(prevProps) {
  // Typical usage (don't forget to compare props):
  if (this.props.userID !== prevProps.userID) {
    this.fetchData(this.props.userID);
  }
}
  • 同樣地我們可以在此階段setState更新state資料,但可以和樓上propsㄧ樣判斷是否有需要更新,以避免額外的重新渲染。

備註:此階段使用setState

Unmounting(卸載)

生命走到終章,當我不需要這個component的時候,在它卸載和銷毀前,我還可以做些什麼?

componentWillUnmount()

component被卸載前被調用,基本上看起來像是清垃圾的。比如說不需要用的計時器、取消網路請求或在componentDidMount()執行的任何訂閱(subscriptions),都可以在這裡清掉。
備註:此階段不可使用setState,它都不會被重新渲染了,也不需要更新state。

了解component的lifecycle後,其實對人生還是有點疑惑的
https://media.giphy.com/media/3ohhwrsMa5vLKra848/giphy.gif
要怎麼見證component的unmounting呢?
謝謝資源氾濫充足的現在,在youtube找到了實例說明React in 5: Tidying up with componentWillUnmount
附上自己在codepen上實作的程式碼(codepen傳送門)
透過父層App component上判斷要不要掛載Timer component來表現component的生命週期(lifecycle)。


生命果然是既深奧又複雜,上面看到的是常用&常見的生命週期method,其實完整的生命週期長這樣:
https://ithelp.ithome.com.tw/upload/images/20181013/20111595nafVqIuj7E.png

component的人生變得更複雜了!不!其實是我的人生變複雜了!!!
加上不常用的生命週期method我們多了三位好夥伴分別為

  • static getDerivedStateFromProps(props, state)
    • render前調用,不管是在掛載時或更新時都可以調用。
    • 需return object來更新state或return null不做更新。
    • 當state需隨著props的變化而改變。舉例來說,在動畫轉場的時候可以比較他們的前後children來決定誰要用進場動畫,誰要用出場動畫。
    • 使用前請三思,它會讓程式碼變得冗長又不好懂。
  • shouldComponentUpdate(nextProps, nextState)
    • 收到新的props和state時調用,在最初渲染(initial render)和強迫更新(forceupdate())時不會調用。
    • 若return false則不會重新渲染,預設為true。
    • 為了優化性能而存在,不要依賴他來防止渲染(render),可能會產生其他錯誤(可考慮使用PureComponent)。
    • 不要在此處理deep equality checks或處理JSON.stringify(),既沒效率又損耗效能。
  • getSnapshotBeforeUpdate(prevProps, prevState)
    • render output被commit後調用(如:DOM)。
    • 讓component在DOM被改變前從中捕捉一些訊息(如:scroll position)。
    • 任何的return value都會被送到componentDidUpdate作為參數使用,若無則return null。
    • 可能的使用情境:在聊天線程UI需要特別處理scroll位置。

今日總結:

  • 每個component都會歷經掛載(mounting)、更新(updating)和卸載(unmounting)階段
  • 常用的lifecycle method包括:
    • constructor
    • render(唯一必備,不能沒有它)
    • componentDidMount
    • componentDidUpdate
    • componentWillUnmount
  • 不常用的lifecycle
    • getDerivedStateFromProps
    • shouldComponentUpdate
    • getSnapshotBeforeUpdate

今天列的這些其實不是全部,完整的內容可參考React.Component
如果有解讀錯誤的地方再麻煩糾正我 ಥ_ಥ
再次感嘆,生命果然複雜!本日終了。


上一篇
Day 05-靈魂角色的家庭成員(Props & State)
下一篇
Day 07-來點互動吧(Handling Events)
系列文
React 30天30

2 則留言

1
cythilya
iT邦新手 4 級 ‧ 2018-10-13 19:39:48

/images/emoticon/emoticon42.gif

團員的文章都和團長一樣活潑XD

/images/emoticon/emoticon39.gif

0
hannahpun
iT邦新手 5 級 ‧ 2018-12-15 08:08:15

感謝分享

但 componentDidMount 是不建議使用 setState 因為會 rerendering 有效能上的疑慮

我要留言

立即登入留言