React 組件生命週期詳解

本文詳細介紹了 React 生命週期的用法以及各個階段的生命週期進行,而且用實例代碼作了詳細演示。代碼位置html

話很少說,先上圖

React生命週期圖解

上圖是基於 React 16.4 以後的生命週期圖解。如感受不對,請先查看 React 版本react


React 生命週期詳解

各個階段的生命週期函數

constructor 構造函數

在 React 組件掛載以前被調用,實現 React.Component 的子類的構造函數時,要在第一行加上 super(props)。git

React 構造函數一般只用於兩個目的:github

  • 經過分配一個對象到 this.state 來初始化本地 state
  • 將 事件處理程序 方法綁定到實例

若是沒有初始化狀態(state),而且沒有綁定方法,一般不須要爲 React 組件實現一個構造函數。瀏覽器

不須要在構造函數中調用 setState(),只需將初始狀態設置給 this.state 便可 。性能優化

static getDerivedStateFromProps(nextProps, prevState)

getDerivedStateFromProps 在每次調用 render 方法以前調用。包括初始化和後續更新時。網絡

包含兩個參數:第一個參數爲即將更新的 props 值,第二個參數爲以前的 state函數

返回值:返回爲 null 時,不作任何反作用處理。假若想更新某些 state 狀態值,則返回一個對象,就會對 state 進行修改性能

該生命週期是靜態函數,屬於類的方法,其做用域內是找不到 this測試

render()

render() 方法是類組件中惟一必須的方法,其他生命週期不是必需要寫。 組件渲染時會走到該生命週期,展現的組件都是由 render() 生命週期的返回值來決定。

注意: 若是 shouldComponentUpdate() 方法返回 false ,render() 不會被調用。

componentDidMount()

在 React 組件裝載(mounting)(插入樹)後被當即調用。

componentDidMount 生命週期是進行發送網絡請求、啓用事件監聽的好時機

若是有必要,能夠在今生命週期中馬上調用 setState()

shouldComponentUpdate(nextProps, nextState)

在組件準備更新以前調用,能夠控制組件是否進行更新,返回 true 時組件更新,返回 false 組件不更新。

包含兩個參數,第一個是即將更新的 props 值,第二個是即將跟新後的 state 值,能夠根據更新先後的 props 或 state 進行判斷,決定是否更新,進行性能優化

不要 shouldComponentUpdate 中調用 setState(),不然會致使無限循環調用更新、渲染,直至瀏覽器內存崩潰

getSnapshotBeforeUpdate(prevProps, prevState)

getSnapshotBeforeUpdate() 在最近一次的渲染輸出被提交以前調用。也就是說,在 render 以後,即將對組件進行掛載時調用。

它可使組件在 DOM 真正更新以前捕獲一些信息(例如滾動位置),今生命週期返回的任何值都會做爲參數傳遞給 componentDidUpdate()。如不須要傳遞任何值,那麼請返回 null

componentDidUpdate(prevProps, prevState, snapshot)

componentDidUpdate() 在更新發生以後當即被調用。這個生命週期在組件第一次渲染時不會觸發。

能夠在今生命週期中調用 setState(),可是必須包含在條件語句中,不然會形成無限循環,最終致使瀏覽器內存崩潰

componentWillUnmount()

componentWillUnmount() 在組件即將被卸載或銷燬時進行調用。

今生命週期是取消網絡請求、移除監聽事件清理 DOM 元素清理定時器等操做的好時機

注意: componentWillMount()、componentWillUpdate()、componentWillReceiveProps() 即將被廢棄,請不要再在組件中進行使用。所以本文不作講解,避免混淆。

生命週期執行順序

掛載時

  • constructor()

  • static getDerivedStateFromProps()

  • render()

  • componentDidMount()

更新時

  • static getDerivedStateFromProps()

  • shouldComponentUpdate()

  • render()

  • getSnapshotBeforeUpdate()

  • componentDidUpdate()

卸載時

  • componentWillUnmount()

生命週期中是否可調用 setState()

初始化 state

  • constructor()

能夠調用 setState()

  • componentDidMount()

根據判斷條件能夠調用 setState()

  • componentDidUpdate()

禁止調用 setState()

  • shouldComponentUpdate()

  • render()

  • getSnapshotBeforeUpdate()

  • componentWillUnmount()


實例演示

源碼地址

下面根據一個父子組件的props 改變、state 改變以及子組件的掛載/卸載等事件,對各生命週期執行順序進行理解,有興趣的同窗能夠一塊兒看一下,也能夠下載代碼本身進行測試。

編寫組件代碼

父組件:Parent.js

import React, { Component } from 'react';

import Child from './Child.js';

const parentStyle = {
    padding: 40,
    margin: 20,
    border: '1px solid pink'
}

const TAG = "Parent 組件:"

export default class Parent extends Component {

    constructor(props) {
        super(props);
        console.log(TAG, 'constructor');
        this.state = {
            num: 0,
            mountChild: true
        }
    }

    static getDerivedStateFromProps(nextProps, prevState) {
        console.log(TAG, 'getDerivedStateFromProps');
        return null;
    }

    componentDidMount() {
        console.log(TAG, 'componentDidMount');
    }

    shouldComponentUpdate(nextProps, nextState) {
        console.log(TAG, 'shouldComponentUpdate');
        return true;
    }

    getSnapshotBeforeUpdate(prevProps, prevState) {
        console.log(TAG, 'getSnapshotBeforeUpdate');
        return null;
    }
    
    componentDidUpdate(prevProps, prevState, snapshot) {
        console.log(TAG, 'componentDidUpdate');
    }

    componentWillUnmount() {
        console.log(TAG, 'componentWillUnmount');
    }


    /** * 修改傳給子組件屬性 num 的方法 */
    changeNum = () => {
        let { num } = this.state;
        this.setState({
            num: ++ num
        });
    }

    /** * 切換子組件掛載和卸載的方法 */
    toggleMountChild = () => {
        let { mountChild } = this.state;
        this.setState({
            mountChild: !mountChild
        });
    }

    render() {
        console.log(TAG, 'render');
        const { num, mountChild } = this.state;
        return (
            <div style={ parentStyle }> <div> <p>父組件</p> <button onClick={ this.changeNum }>改變傳給子組件的屬性 num</button> <br /> <br /> <button onClick={ this.toggleMountChild }>卸載 / 掛載子組件</button> </div> { mountChild ? <Child num={ num } /> : null } </div> ) } } 複製代碼

子組件:Child.js

import React, { Component } from 'react'


const childStyle = {
    padding: 20,
    margin: 20,
    border: '1px solid black'
}

const TAG = 'Child 組件:'

export default class Child extends Component {

    constructor(props) {
        super(props);
        console.log(TAG, 'constructor');
        this.state = {
            counter: 0
        };
    }

    static getDerivedStateFromProps(nextProps, prevState) {
        console.log(TAG, 'getDerivedStateFromProps');
        return null;
    }

    componentDidMount() {
        console.log(TAG, 'componentDidMount');
    }

    shouldComponentUpdate(nextProps, nextState) {
        console.log(TAG, 'shouldComponentUpdate');
        return true;
    }

    getSnapshotBeforeUpdate(prevProps, prevState) {
        console.log(TAG, 'getSnapshotBeforeUpdate');
        return null;
    }
    
    componentDidUpdate(prevProps, prevState, snapshot) {
        console.log(TAG, 'componentDidUpdate');
    }

    componentWillUnmount() {
        console.log(TAG, 'componentWillUnmount');
    }

    changeCounter = () => {
        let { counter }= this.state;
        this.setState({
            counter: ++ counter
        });
    }

    render() {
        console.log(TAG, 'render');
        const { num } = this.props;
        const { counter } = this.state;
        return (
            <div style={ childStyle }> <p>子組件</p> <p>父組件傳過來的屬性 num : { num }</p> <p>自身狀態 counter : { counter }</p> <button onClick={ this.changeCounter }>改變自身狀態 counter</button> </div>
        )
    }
}
複製代碼

從五種組件狀態改變的時機來探究生命週期的執行順序

1、父子組件初始化

父子組件第一次進行渲染加載時,界面展現爲:

初始化展現界面

控制檯中的 log 打印順序爲:

  • Parent 組件: constructor()
  • Parent 組件: getDerivedStateFromProps()
  • Parent 組件: render()
  • Child 組件: constructor()
  • Child 組件: getDerivedStateFromProps()
  • Child 組件: render()
  • Child 組件: componentDidMount()
  • Parent 組件: componentDidMount()

2、修改子組件自身狀態 state 時

點擊子組件中的 改變自身狀態 按鈕,則界面上 自身狀態 counter: 的值會 + 1,控制檯中的 log 打印順序爲:

  • Child 組件: getDerivedStateFromProps()
  • Child 組件: shouldComponentUpdate()
  • Child 組件: render()
  • Child 組件: getSnapshotBeforeUpdate()
  • Child 組件: componentDidUpdate()

3、修改父組件中傳入子組件的 props 時

點擊父組件中的 改變傳給子組件的屬性 num 按鈕,則界面上 父組件傳過來的屬性 num: 的值會 + 1,控制檯中的 log 打印順序爲:

  • Parent 組件: getDerivedStateFromProps()
  • Parent 組件: shouldComponentUpdate()
  • Parent 組件: render()
  • Child 組件: getDerivedStateFromProps()
  • Child 組件: shouldComponentUpdate()
  • Child 組件: render()
  • Child 組件: getSnapshotBeforeUpdate()
  • Parent 組件: getSnapshotBeforeUpdate()
  • Child 組件: componentDidUpdate()
  • Parent 組件: componentDidUpdate()

4、卸載子組件

點擊父組件中的 卸載 / 掛載子組件 按鈕,則界面上子組件會消失,控制檯中的 log 打印順序爲:

  • Parent 組件: getDerivedStateFromProps()
  • Parent 組件: shouldComponentUpdate()
  • Parent 組件: render()
  • Parent 組件: getSnapshotBeforeUpdate()
  • Child 組件: componentWillUnmount()
  • Parent 組件: componentDidUpdate()

5、從新掛載子組件

再次點擊父組件中的 卸載 / 掛載子組件 按鈕,則界面上子組件會從新渲染出來,控制檯中的 log 打印順序爲:

  • Parent 組件: getDerivedStateFromProps()
  • Parent 組件: shouldComponentUpdate()
  • Parent 組件: render()
  • Child 組件: constructor()
  • Child 組件: getDerivedStateFromProps()
  • Child 組件: render()
  • Parent 組件: getSnapshotBeforeUpdate()
  • Child 組件: componentDidMount()
  • Parent 組件: componentDidUpdate()

父子組件生命週期執行順序總結:

  • 當子組件自身狀態改變時,不會對父組件產生反作用的狀況下,父組件不會進行更新,即不會觸發父組件的生命週期

  • 當父組件中狀態發生變化(包括子組件的掛載以及)時,會觸發自身對應的生命週期以及子組件的更新

    • render 以及 render 以前的生命週期,則 父組件 先執行

    • render 以及 render 以後的聲明週期,則子組件先執行,而且是與父組件交替執行

  • 當子組件進行卸載時,只會執行自身的 componentWillUnmount 生命週期,不會再觸發別的生命週期

可能總結的很差,不是很完整。只是根據通常狀況進行的總結。有不妥之處,但願各位朋友可以多多指正。


示例代碼下載

源碼地址(歡迎 Star,謝謝!)

還沒看夠?移步至:React Component 官網

相關文章
相關標籤/搜索