捋一捋React的生命週期

前言:React 的版本從 v15 到 v16.3 ,再到v16.4,現在最新的版本是 v16.8了。其中最大的變化可能是React Hooks的加入,而最令人困惑的卻是它的生命週期,新舊生命週期函數混雜在一起,難免會讓許多新來者有很多困惑。所以這一篇我們來分析一下 React 生命週期的演變及原因,進一步理解其使用。

組件生命週期的四個階段

  1. Initialization (初始化階段:組件實例的創建)
  2. Mounting (加載階段:組件插入 dom中)
  3. Updating (更新階段:屬性或狀態每改變一次都將會觸發一次,組件重新渲染。)
  4. Unmounting (卸載階段:組件卸載和銷燬)

老版生命週期(16.3之前的生命週期)

clipboard.png

Initialization (初始化階段:涉及4個鉤子函數)

這些方法會在組件初始化的時候被調用,只跟實例的創建有關。
如果用createReactClass進行創建,則還有getInitialState這些生命週期函數,但很少使用,我們這裏不提及。

static defaultProps{} (getDefaultProps())

定義默認props,會和父組件傳下來的props進行合併,且以父組件中的props優先級更高,相當於{...defaultProps, props}

static propTypes{} (getInitialState())

定義props的數據類型,可以幫助我們確定其有效性,減少許多開發中不必要的錯誤。

constructor()

在加載階段前調用一次,進行 state 的初始化。

constructor(props){
    super(props)
}

super(props)用來調用父類的構建方法。

Mounting (加載階段:涉及3個鉤子函數)

componentWillMount()

新版中爲UNSAFE_componentWillMount()

只在組件加載時調用,整個生命週期只調用一次,以後組件更新也不會調用,此時可以修改 state。

render()

react 中最重要的生命週期函數,創建虛擬 dom,並進行 diff 算法,更新 dom 樹也在此進行。所以不應該在此改變組件的狀態或者與瀏覽器進行交互。

注意:這個函數不能缺少,如果不創建虛擬 dom,可以return null

componentDidMount()

組件加載完成後立即調用,整個生命週期只調用一次,可以獲取到更新後的 DOM,在此處可以進行網絡請求等。

Updating (更新階段:涉及5個鉤子函數)

componentWillReceiveProps()

新版中爲UNSAFE_componentWillReceiveProps()

在組件加載後,如果有新的props傳遞過來,會先調用這個函數,可以在這裏調用setState()修改state

componentWillReceiveProps(nextProps)

shouldComponentUpdate()

react中一個重要的性能優化點,組件接收到新的props或者state,返回true表示需要更新 dom,返回false阻止更新。

shouldComponentUpdate(nextProps, nextState)

componentWillUpdate()

新版中爲UNSAFE_componentWillUpdate()

組件加載時不調用,只有在組件需要更新(即shouldComponentUpdate返回true)時調用。

componentWillUpdate(nextProps, nextState)

注意:不能在這個方法中調用setState()修改state

render()

同上。

componentDidUpdate()

在組件更新完成後立即被調用,可以進行網絡請求等。

componentDidUpdate(prevProps, prevState)

Unmounting (卸載階段:涉及1個鉤子函數)

componentWillUnmount()

在組件被卸載和銷燬之前調用,可以在這裏處理任何必要的清理工作,比如解除定時器,取消已經發起的網絡請求,清理在componentDidMount函數中創建的 DOM 元素。

componentWillUnmount()

Error Handling(錯誤處理:涉及1個鉤子函數)

componentDidCatch()

錯誤邊界捕獲,在v16.0剛推出的時候新增加的一個生命週期函數,用於捕獲在子組件樹中任意地方發生的 JavaScript 錯誤,一個錯誤邊界不能捕獲它自己內部的錯誤。

componentDidCatch(error, info)

組件的基本寫法

import React, {Component} from 'React'

export default class OldReactComponent extends Componet{
    static defaultProps={}
    static propTypes={}
    constructor(props){
        super(props)
    }
    state={}
    componentWillMount(){}
    render(){
        return null
    }
    componentDidMount(){}
    componentWillReceivePorps(nextProps){}
    componentShouldUpdate(nextProps,nextState){
        return true
    }
    componentWillUpdate(nextProps,nextState){}
    componentDidUpdate(){}
    componentWillUnmount(){}
}

新版生命週期(16.4之後的生命週期)

clipboard.png

(帶UNSAFE_的函數在以後將會被丟棄,官方也不建議使用,這裏不再列出。)

新版本中,新增了三個生命週期函數:

  • static getDerivedStateFromProps()
  • getSnapshotBeforeUpdate
  • static getDerivedStateFromError()

同時deprecate了一組生命週期API,包括:

  • componentWillReceiveProps
  • componentWillMount
  • componentWillUpdate

可以看出除了shouldComponentUpdate之外,render 之前的所有生命週期都被消滅了。原因是這些生命週期太多時候沒有被正確使用,而且在Fiber之後,如果要開啓async rendering,在render函數之前的所有函數,都有可能被執行多次。

Initialization (初始化階段:涉及4個鉤子函數)

同上。

Mounting (加載階段:涉及3個鉤子函數)

static getDerivedStateFromProps()

因爲是靜態方法,所以無法訪問到組件實例。每次組件調用render前都會被調用,獲取新的propsstate(v16.3只會爲props的而調用,v16.4修正爲propsstate)之後,返回一個對象作爲新的state,如果返回null則表示不需要更新;配合componentDidUpdate,可以覆蓋componentWillReceiveProps的所有用法。並且它應該是個純函數,沒有副作用(side effect)。

static getDerivedStateFromProps(props,state)

render()

同上。

componentDidMount()

同上。

Updating (更新階段:涉及5個鉤子函數)

static getDerivedStateFromProps()

同上。

shouldComponentUpdate()

同上。

render()

同上。

getSnapshotBeforeUpdate()

觸發時間: update發生的時候,在render之後,在組件dom渲染之前;返回一個值,作爲componentDidUpdate的第三個參數;配合componentDidUpdate, 可以覆蓋componentWillUpdate的所有用法,常用於 scroll 位置的定位。

getSnapshotBeforeUpdate(prevProps, prevState)

componentDidUpdate()

同上。

Unmounting (卸載階段:涉及1個鉤子函數)

componentWillUnmount()

同上。

Error Handling(錯誤處理:涉及2個鉤子函數)

componentDidCatch()

同上。

static getDerivedStateFromError()

用於在“render”階段(非render函數)的錯誤捕獲,應該是個純函數,沒有副作用(side effect)。

static getDerivedStateFromError(error)

組件的基本寫法

import React, {Component} from 'React'

export default class OldReactComponent extends Componet{
    static defaultProps={}
    static propTypes={}
    constructor(props){
        super(props)
    }
    state={}
    static getDerivedStateFromProps(props,state){}
    render(){
        return null
    }
    componentDidMount(){}
    componentShouldUpdate(nextProps,nextState){
        return true
    }
    getSnapshotBeforeUpdate(prevProps, prevState){}
    componentDidUpdate(){}
    componentWillUnmount(){}
}

總結

  1. React新的生命週期新增static getDerivedStateFromPropsgetSnapshotBeforeUpdate並棄用componentWillMountcomponentWillReceivePropscomponetWillUpdate(這三個函數將在 React 17中刪除)。
  2. 新增static getDerivedStateFromError捕獲“render”階段的錯誤。

參考

幫你理清React的生命週期

React-新的生命週期(React16版本)

React v16.3之後的組件生命週期函數

清晰大圖請點擊這裏

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章