React Component生命週期

在學習vue時候有生命週期的認知,其實react一樣也有生命週期。 React的生命週期組件分三個階段:

  • Mounting(加載階段)
  • Updating(更新階段)
  • Unmounting(卸載階段)

舊生命週期

版本更新我們先看下之前舊的生命週期
在這裏插入圖片描述
以一個例子爲例

import React, { Component } from 'react'

class LifeCycleSon extends Component {
    constructor(props){
        super(props)
        this.state={
            word: '我是生命週期內的state'
        }
        console.log('1.constructor構造函數')
	}
    componentWillMount(){
        //組件將要掛在,這個時候我們可以進行api的調用,獲取數據,但是不能進行dom操作
        console.log('2.componentWillMount組件將要掛載')
    }
    componentDidMount(){
        //此時組件已經掛載,我們可以進行dom操作,可以對我們的狀態進行更新操作了
        console.log('4.componentDidMount組件已經掛載')
    }
    componentWillReceiveProps(nextProps){
        //父組件傳遞的屬性有變化,我們可以在這裏做相應的響應操作
        console.log("5.componentWillReceiveProps父組件傳遞的屬性更新了")
    }
    shouldComponentUpdate(nextProps, nextState){
        //組件是否需要更新,需要返回一個布爾值,返回true則更新,返回flase不更新,這是一個優化點
        console.log('6.shouldComponentUpdate組件是否應該更新,需要返回布爾值')
        return true
    }
    componentWillUpdate(nextProps, nextState){
        //組件將要更新
        console.log('7.componentWillUpdate組件將要更新')
    }
    componentDidUpdate(){
        //組件已經更新
        console.log('8.componentDidUpdate組件已經更新')
    }
    componentWillUnmount(){
        //組件銷燬
        console.log("9.componentWillUnmount組件已經銷燬")
    }
    render() {
        console.log('3.render組件渲染')
        return (
            <div>
                {this.props.title}
            </div>
        )
    }
}

export default class LifeCycle extends Component {
    constructor(props){
        super(props)

        this.state={
            son:'我是生命週期父組件',
            showSon:true
        }
        setTimeout(()=>{
            this.setState({
                son:'父組件更新',
            })
        },2000)
        setTimeout(()=>{
            this.setState({
                showSon:false
            })
        },4000)
    }
    render() {
        return (
            <div>
               {this.state.showSon?<LifeCycleSon title={this.state.son}></LifeCycleSon>:<div>組件已銷燬</div>} 
            </div>
        )
    }
}

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

constructor()

加載的時候調用一次,可以初始化state

getDefaultProps()

設置默認的props,也可以用dufaultProps設置組件的默認屬性。

getInitialState()

初始化state,可以直接在constructor中定義this.state

componentWillMount()

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

render()

react最重要的步驟,創建虛擬dom,進行diff算法,更新dom樹都在此進行。根據組件的props和state(無兩者的重傳遞和重賦值,論值是否有變化,都可以引起組件重新render) ,return 一個React元素(描述組件,即UI),不負責組件實際渲染工作,之後由React自身根據此元素去渲染出頁面DOM。render是純函數(Pure function:函數的返回結果只依賴於它的參數;函數執行過程裏面沒有副作用),不能在裏面執行this.setState,會有改變組件狀態的副作用。

componentDidMount()

組件渲染之後調用,只調用一次

上述的代碼掛載結果爲
在這裏插入圖片描述
Updating(更新階段:涉及5個鉤子函數)
componentWillReceivePorps(nextProps)

組件加載時不調用,組件接受新的props時調用,所以在此方法中根據nextProps和this.props來查明重傳的props是否改變,以及如果改變了要執行啥,比如根據新的props調用this.setState出發當前組件的重新render。根據官網的描述:在該函數(componentWillReceiveProps)中調用 this.setState() 將不會引起第二次渲染。

shouldComponentUpdate(nextProps, nextState)

組件接收到新的props或者state時調用,return true就會更新dom(使用diff算法更新),return false能阻止更新(不調用render)以此可用來減少組件的不必要渲染,優化組件性能。

ps:這邊也可以看出,就算componentWillReceiveProps()中執行了this.setState,更新了state,但在render前(如shouldComponentUpdate,componentWillUpdate),this.state依然指向更新前的state,不然nextState及當前組件的this.state的對比就一直是true了。

componentWillUpdata(nextProps, nextState)

組件加載時不調用,只有在組件將要更新時才調用,此時可以修改state,在這邊可執行一些組件更新發生前的工作,一般較少用。

render()

render方法在上文講過,這邊只是重新調用。

componentDidUpdate(prevProps, prevState)

組件加載時不調用,組件更新完成後調用,可以操作組件更新的DOM,prevProps和prevState這兩個參數指的是組件更新前的props和state

上述的代碼更新結果爲
在這裏插入圖片描述

造成組件更新的情況

  1. 父組件重新render
a. 直接使用,每當父組件重新render導致的重傳props,子組件將直接跟着重新渲染,無論props是否有變化。可通過shouldComponentUpdate方法優化。


class Child extends Component {
   shouldComponentUpdate(nextProps){ // 應該使用這個方法,否則無論props是否有變化都將會導致組件跟着重新渲染
        if(nextProps.someThings === this.props.someThings){
          return false
        }
    }
    render() {
        return <div>{this.props.someThings}</div>
    }
}

b.在componentWillReceiveProps方法中,將props轉換成自己的state


class Child extends Component {
    constructor(props) {
        super(props);
        this.state = {
            someThings: props.someThings
        };
    }
    componentWillReceiveProps(nextProps) { // 父組件重傳props時就會調用這個方法
        this.setState({someThings: nextProps.someThings});
    }
    render() {
        return <div>{this.state.someThings}</div>
    }
}

  1. 組件本身調用setState,無論state有沒有變化。可通過shouldComponentUpdate方法優化。
class Child extends Component {
   constructor(props) {
        super(props);
        this.state = {
          someThings:1
        }
   }
   shouldComponentUpdate(nextStates){ // 應該使用這個方法,否則無論state是否有變化都將會導致組件重新渲染
        if(nextStates.someThings === this.state.someThings){
          return false
        }
    }

   handleClick = () => { // 雖然調用了setState ,但state並無變化
        const preSomeThings = this.state.someThings
         this.setState({
            someThings: preSomeThings
         })
   }

    render() {
        return <div onClick = {this.handleClick}>{this.state.someThings}</div>
    }
}

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

組件渲染之後調用,只調用一次。此方法在組件被卸載前調用,可以在這裏執行一些清理工作,比如清楚組件中使用的定時器,清楚componentDidMount中手動創建的DOM元素等,以避免引起內存泄漏。

上述的代碼銷燬結果爲
在這裏插入圖片描述

新生命週期v16.4

官網鏈接:
http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
https://zh-hans.reactjs.org/docs/react-component.html#getsnapshotbeforeupdate

在這裏插入圖片描述

import React, { Component } from 'react'

class LifeCycleSon extends Component {
    constructor(props){
        super(props)
        // getDefaultProps:接收初始props
        // getInitialState:初始化state
        this.state={
            word: '我是生命週期內的state'
        }
        console.log('1.constructor構造函數')
    }
    static getDerivedStateFromProps(nextProps, prevState){
        console.log('2.getDerivedStateFromProps-----',nextProps,prevState)
        return nextProps
    }
    componentDidCatch(error, info) { // 獲取到javascript錯誤
        console.log('componentDidCatch',error, info)
    }
    componentDidMount(){
        //此時組件已經掛載,我們可以進行dom操作,可以對我們的狀態進行更新操作了
        console.log('4.componentDidMount組件已經掛載')
    }
    shouldComponentUpdate(nextProps, nextState){
        //組件是否需要更新,需要返回一個布爾值,返回true則更新,返回flase不更新,這是一個優化點
        console.log('6.shouldComponentUpdate組件是否應該更新,需要返回布爾值',nextProps, nextState)
        return true
    }
    getSnapshotBeforeUpdate(prevProps, prevState) { 
        // 組件更新前觸發
        console.log('7.getSnapshotBeforeUpdate',prevProps, prevState)
        return null
    }
    componentDidUpdate(prevProps, prevState, snapshot){
        //組件已經更新
        console.log('8.componentDidUpdate組件已經更新',prevProps, prevState, snapshot)
    }
    componentWillUnmount(){
        //組件銷燬
        console.log("9.componentWillUnmount組件已經銷燬")
    }
    render() {
        console.log('3.render組件渲染')
        return (
            <div>
                {this.props.title}
            </div>
        )
    }
}

export default class LifeCycle extends Component {
    constructor(props){
        super(props)

        this.state={
            son:'我是生命週期父組件',
            showSon:true
        }
        setTimeout(()=>{
            this.setState({
                son:'父組件更新',
            })
        },2000)
        setTimeout(()=>{
            this.setState({
                showSon:false
            })
        },4000)
    }
    render() {
        return (
            <div>
               {this.state.showSon?<LifeCycleSon title={this.state.son}></LifeCycleSon>:<div>組件已銷燬</div>} 
            </div>
        )
    }
}

Mounting(加載階段:涉及6個鉤子函數)
當組件實例被創建並插入 DOM 中時,其生命週期調用順序如下:

constructor()

加載的時候調用一次,可以初始化state

static getDerivedStateFromProps(nextProps, prevState)

用一個靜態函數getDerivedStateFromProps來取代被deprecate的幾個生命週期函數,就是強制開發者在render之前只做無副作用的操作,而且能做的操作侷限在根據props和state決定新的state,而已。組件每次被rerender的時候,包括在組件構建之後(虛擬dom之後,實際dom掛載之前),每次獲取新的props或state之後;每次接收新的props之後都會返回一個對象作爲新的state,返回null則說明不需要更新state;配合componentDidUpdate,可以覆蓋componentWillReceiveProps的所有用法。

注意:

  • getDerivedStateFromProps前面要加上static保留字,聲明爲靜態方法,不然會被react忽略掉
  • getDerivedStateFromProps裏面的this爲undefined。static靜態方法只能Class(構造函數)來調用(App.staticMethod✅),而實例是不能的( (new App()).staticMethod ❌ );當調用React Class組件時,改組件會實例化;所以React Class組件中,靜態方法getDerivedStateFromProps無權訪問Class實例的this,即this爲undefined。可以看react issue相關討論 https://github.com/facebook/react/issues/12612 https://github.com/facebook/react/issues/14730

render()

react最重要的步驟,創建虛擬dom,進行diff算法,更新dom樹都在此進行,和上面無差

componentDidMount()

組件渲染之後調用,只調用一次

結果爲:
在這裏插入圖片描述
Updating(更新階段:涉及5個鉤子函數)
static getDerivedStateFromProps(props, state)

組件每次被rerender的時候,包括在組件構建之後(虛擬dom之後,實際dom掛載之前),每次獲取新的props或state之後;每次接收新的props之後都會返回一個對象作爲新的state,返回null則說明不需要更新state;配合componentDidUpdate,可以覆蓋componentWillReceiveProps的所有用法

shouldComponentUpdate(nextProps, nextState)

組件接收到新的props或者state時調用,return true就會更新dom(使用diff算法更新),return
false能阻止更新(不調用render)

render()

react最重要的步驟,創建虛擬dom,進行diff算法,更新dom樹都在此進行

getSnapshotBeforeUpdate(prevProps, prevState)

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

例如官方示例

class ScrollingList extends React.Component {
  constructor(props) {
    super(props);
    this.listRef = React.createRef();
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // 我們是否在 list 中添加新的 items ?
    // 捕獲滾動​​位置以便我們稍後調整滾動位置。
    if (prevProps.list.length < this.props.list.length) {
      const list = this.listRef.current;
      return list.scrollHeight - list.scrollTop;
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // 如果我們 snapshot 有值,說明我們剛剛添加了新的 items,
    // 調整滾動位置使得這些新 items 不會將舊的 items 推出視圖。
    //(這裏的 snapshot 是 getSnapshotBeforeUpdate 的返回值)
    if (snapshot !== null) {
      const list = this.listRef.current;
      list.scrollTop = list.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      <div ref={this.listRef}>{/* ...contents... */}</div>
    );
  }
}

componentDidUpdate()

組件加載時不調用,組件更新完成後調用

結果:
在這裏插入圖片描述
Unmounting(卸載階段:涉及1個鉤子函數)

componentWillUnmount()

組件渲染之後調用,只調用一次。此方法在組件被卸載前調用,可以在這裏執行一些清理工作,比如清楚組件中使用的定時器,清楚componentDidMount中手動創建的DOM元素等,以避免引起內存泄漏。

上述的代碼銷燬結果爲
在這裏插入圖片描述

componentDidCatch(error,info)

組件渲染之後調用,只調用一次

參考鏈接
https://zhuanlan.zhihu.com/p/38030418
https://www.jianshu.com/p/50fe3fb9f7c3

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