16.3 新版react生命週期 圖示

16.3 新版react生命週期

React v16.0前的生命週期

其實大部分團隊不見得會跟進升到16版本,因此16前的生命週期仍是頗有必要掌握的,況且16也是基於以前的修改html

img

第一個是組件初始化(initialization)階段

也就是如下代碼中類的構造方法( constructor() ),Test類繼承了react Component這個基類,也就繼承這個react的基類,纔能有render(),生命週期等方法可使用,這也說明爲何函數組件不能使用這些方法的緣由。react

super(props)用來調用基類的構造方法( constructor() ), 也將父組件的props注入給子組件,功子組件讀取(組件中props只讀不可變,state可變)。
constructor()用來作一些組件的初始化工做,如定義this.state的初始內容。segmentfault

import React, { Component } from 'react';

class Test extends Component {
  constructor(props) {
    super(props);
  }
}

第二個是組件的掛載(Mounting)階段

此階段分爲componentWillMount,render,componentDidMount三個時期。

  • componentWillMount:

在組件掛載到DOM前調用,且只會被調用一次,在這邊調用this.setState不會引發組件從新渲染,也能夠把寫在這邊的內容提早到constructor()中,因此項目中不多用。數組

  • render:

根據組件的props和state(無二者的重傳遞和重賦值,論值是否有變化,均可以引發組件從新render) ,return 一個React元素(描述組件,即UI),不負責組件實際渲染工做,以後由React自身根據此元素去渲染出頁面DOM。render是純函數(Pure function:函數的返回結果只依賴於它的參數;函數執行過程裏面沒有反作用),不能在裏面執行this.setState,會有改變組件狀態的反作用。服務器

  • componentDidMount:

組件掛載到DOM後調用,且只會被調用一次async

第三個是組件的更新(update)階段

在講述此階段前須要先明確下react組件更新機制。setState引發的state更新或父組件從新render引發的props更新,更新後的state和props相對以前不管是否有變化,都將引發子組件的從新render。詳細可看這篇文章函數

形成組件更新有兩類(三種)狀況:

  • 1.父組件從新render

父組件從新render引發子組件從新render的狀況有兩種,內容及代碼修引自xiaoyann的回答性能

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轉換成本身的statethis

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>
    }
}

根據官網的描述

在該函數(componentWillReceiveProps)中調用 this.setState() 將不會引發第二次渲染。

是由於componentWillReceiveProps中判斷props是否變化了,若變化了,this.setState將引發state變化,從而引發render,此時就不必再作第二次因重傳props引發的render了,否則重複作同樣的渲染了。

  • 2.組件自己調用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>
    }
}

此階段分爲componentWillReceiveProps,shouldComponentUpdate,componentWillUpdate,render,componentDidUpdate

  • componentWillReceiveProps(nextProps)

此方法只調用於props引發的組件更新過程當中,參數nextProps是父組件傳給當前組件的新props。但父組件render方法的調用不能保證重傳給當前組件的props是有變化的,因此在此方法中根據nextProps和this.props來查明重傳的props是否改變,以及若是改變了要執行啥,好比根據新的props調用this.setState出發當前組件的從新render

  • shouldComponentUpdate(nextProps, nextState)

此方法經過比較nextProps,nextState及當前組件的this.props,this.state,返回true時當前組件將繼續執行更新過程,返回false則當前組件更新中止,以此可用來減小組件的沒必要要渲染,優化組件性能。

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

  • componentWillUpdate(nextProps, nextState)

此方法在調用render方法前執行,在這邊可執行一些組件更新發生前的工做,通常較少用。

  • render

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

  • componentDidUpdate(prevProps, prevState)

此方法在組件更新後被調用,能夠操做組件更新的DOM,prevProps和prevState這兩個參數指的是組件更新前的props和state

卸載階段

此階段只有一個生命週期方法:componentWillUnmount

  • componentWillUnmount

此方法在組件被卸載前調用,能夠在這裏執行一些清理工做,好比清楚組件中使用的定時器,清楚componentDidMount中手動建立的DOM元素等,以免引發內存泄漏。

React v16.4 的生命週期

React v16.4 的生命週期圖

img

React v16.4 的生命週期

變動原因

原來(React v16.0前)的生命週期在React v16推出的Fiber以後就不合適了,由於若是要開啓async rendering,在render函數以前的全部函數,都有可能被執行屢次。

原來(React v16.0前)的生命週期有哪些是在render前執行的呢?

  • componentWillMount
  • componentWillReceiveProps
  • shouldComponentUpdate
  • componentWillUpdate

若是開發者開了async rendering,並且又在以上這些render前執行的生命週期方法作AJAX請求的話,那AJAX將被無謂地屢次調用。。。明顯不是咱們指望的結果。並且在componentWillMount裏發起AJAX,無論多快獲得結果也趕不上首次render,並且componentWillMount在服務器端渲染也會被調用到(固然,也許這是預期的結果),這樣的IO操做放在componentDidMount裏更合適。

禁止不能用比勸導開發者不要這樣用的效果更好,因此除了shouldComponentUpdate,其餘在render函數以前的全部函數(componentWillMount,componentWillReceiveProps,componentWillUpdate)都被getDerivedStateFromProps替代。

也就是用一個靜態函數getDerivedStateFromProps來取代被deprecate的幾個生命週期函數,就是強制開發者在render以前只作無反作用的操做,並且能作的操做侷限在根據props和state決定新的state

React v16.0剛推出的時候,是增長了一個componentDidCatch生命週期函數,這只是一個增量式修改,徹底不影響原有生命週期函數;可是,到了React v16.3,大改動來了,引入了兩個新的生命週期函數。

新引入了兩個新的生命週期函數:getDerivedStateFromPropsgetSnapshotBeforeUpdate

getDerivedStateFromProps

getDerivedStateFromProps原本(React v16.3中)是隻在建立和更新(由父組件引起部分),也就是否是不禁父組件引起,那麼getDerivedStateFromProps也不會被調用,如自身setState引起或者forceUpdate引起。

React v16.3 的生命週期圖

img

React v16.3

這樣的話理解起來有點亂,在React v16.4中改正了這一點,讓getDerivedStateFromProps不管是Mounting仍是Updating,也不管是由於什麼引發的Updating,所有都會被調用,具體可看React v16.4 的生命週期圖。

React v16.4後的getDerivedStateFromProps

static getDerivedStateFromProps(props, state) 在組件建立時和更新時的render方法以前調用,它應該返回一個對象來更新狀態,或者返回null來不更新任何內容。

getSnapshotBeforeUpdate

getSnapshotBeforeUpdate() 被調用於render以後,能夠讀取但沒法使用DOM的時候。它使您的組件能夠在可能更改以前從DOM捕獲一些信息(例如滾動位置)。今生命週期返回的任何值都將做爲參數傳遞給componentDidUpdate()。

官網給的例子:

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

  getSnapshotBeforeUpdate(prevProps, prevState) {
    //咱們是否要添加新的 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>
    );
  }
}
相關文章
相關標籤/搜索