我對 React v16.4 生命週期的理解

關於react生命週期的文章,網上一大堆,本人也看了許多,可是以爲大部分人寫的都是照搬其它人的沒有本身獨到的看法,因此決定根據本人的實戰經驗和我的理解再寫一篇React生命週期的文章,因爲React目前已更新到16.4版本,因此重點講解React v16.4變化的生命週期,以前的生命週期函數會一帶而過react

先整體看下React16的生命週期圖canvas

React16廢棄的三個生命週期函數瀏覽器

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

注:目前在16版本中componentWillMountcomponentWillReceivePropscomponentWillUpdate並未徹底刪除這三個生命週期函數,並且新增了UNSAFE_componentWillMountUNSAFE_componentWillReceivePropsUNSAFE_componentWillUpdate三個函數,官方計劃在17版本徹底刪除這三個函數,只保留UNSAVE_前綴的三個函數,目的是爲了向下兼容,可是對於開發者而言應該儘可能避免使用他們,而是使用新增的生命週期函數替代它們bash

取而代之的是兩個新的生命週期函數服務器

  • static getDerivedStateFromProps
  • getSnapshotBeforeUpdate

咱們將React的生命週期分爲三個階段,而後詳細講解每一個階段具體調用了什麼函數,這三個階段是:微信

  • 掛載階段
  • 更新階段
  • 卸載階段

掛載階段

掛載階段,也能夠理解爲組件的初始化階段,就是將咱們的組件插入到DOM中,只會發生一次網絡

這個階段的生命週期函數調用以下:app

  • constructor
  • getDerivedStateFromProps
  • componentWillMount/UNSAVE_componentWillMount
  • render
  • componentDidMount

constructor

組件構造函數,第一個被執行異步

若是沒有顯示定義它,咱們會擁有一個默認的構造函數svg

若是顯示定義了構造函數,咱們必須在構造函數第一行執行super(props),不然咱們沒法在構造函數裏拿到this對象,這些都屬於ES6的知識

在構造函數裏面咱們通常會作兩件事:

  • 初始化state對象
  • 給自定義方法綁定this
constructor(props) {
    super(props)
    
    this.state = {
      select,
      height: 'atuo',
      externalClass,
      externalClassText
    }

    this.handleChange1 = this.handleChange1.bind(this)
    this.handleChange2 = this.handleChange2.bind(this)
}
複製代碼

禁止在構造函數中調用setState,能夠直接給state設置初始值

getDerivedStateFromProps

static getDerivedStateFromProps(nextProps, prevState)

一個靜態方法,因此不能在這個函數裏面使用this,這個函數有兩個參數props和state,分別指接收到的新參數和當前的state對象,這個函數會返回一個對象用來更新當前的state對象,若是不須要更新能夠返回null

該函數會在掛載時,接收到新的props,調用了setState和forceUpdate時被調用

在React v16.3時只有在掛載時和接收到新的props被調用,聽說這是官方的失誤,後來修復了

這個方法就是爲了取代以前的componentWillMountcomponentWillReceivePropscomponentWillUpdate

當咱們接收到新的屬性想去修改咱們state,可使用getDerivedStateFromProps

class ExampleComponent extends React.Component {
  state = {
    isScrollingDown: false,
    lastRow: null
  }
  static getDerivedStateFromProps(nextProps, prevState) {
    if (nextProps.currentRow !== prevState.lastRow) {
        return {
            isScrollingDown:
            nextProps.currentRow > prevState.lastRow,
            lastRow: nextProps.currentRow
        }
    }
    return null
  }
}
複製代碼

componentWillMount/UNSAFE_componentWillMount

在16版本這兩個方法並存,可是在17版本中componentWillMount被刪除,只保留UNSAFE_componentWillMount,目的是爲了作向下兼容,對於新的應用,用getDerivedStateFromProps代替它們

因爲componentWillMount/ UNSAFE_componentWillMount是在render以前調用,因此就算在這個方法中調用setState也不會觸發從新渲染(re-render)

render

React中最核心的方法,一個組件中必需要有這個方法

返回的類型有如下幾種:

  • 原生的DOM,如div
  • React組件
  • Fragment(片斷)
  • Portals(插槽)
  • 字符串和數字,被渲染成text節點
  • Boolean和null,不會渲染任何東西

關於Fragment和Portals是React16新增的,若是你們不清楚能夠去閱讀官方文檔,在這裏就不展開了

render函數是純函數,裏面只作一件事,就是返回須要渲染的東西,不該該包含其它的業務邏輯,如數據請求,對於這些業務邏輯請移到componentDidMount和componentDid Update中

componentDidMount

組件裝載以後調用,此時咱們能夠獲取到DOM節點並操做,好比對canvas,svg的操做,服務器請求,訂閱均可以寫在這個裏面,可是記得在componentWillUnmount中取消訂閱

componentDidMount() {
    const { progressCanvas, progressSVG } = this

    const canvas = progressCanvas.current
    const ctx = canvas.getContext('2d')
    canvas.width = canvas.getBoundingClientRect().width
    canvas.height = canvas.getBoundingClientRect().height

    const svg = progressSVG.current
    const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
    rect.setAttribute('x', 0)
    rect.setAttribute('y', 0)
    rect.setAttribute('width', 0)
    rect.setAttribute('height', svg.getBoundingClientRect().height)
    rect.setAttribute('style', 'fill:red')

    const animate = document.createElementNS('http://www.w3.org/2000/svg', 'animate')
    animate.setAttribute('attributeName', 'width')
    animate.setAttribute('from', 0)
    animate.setAttribute('to', svg.getBoundingClientRect().width)
    animate.setAttribute('begin', '0ms')
    animate.setAttribute('dur', '1684ms')
    animate.setAttribute('repeatCount', 'indefinite')
    animate.setAttribute('calcMode', 'linear')
    rect.appendChild(animate)
    svg.appendChild(rect)
    svg.pauseAnimations()

    this.canvas = canvas
    this.svg = svg
    this.ctx = ctx
 }
複製代碼

在componentDidMount中調用setState會觸發一次額外的渲染,多調用了一次render函數,可是用戶對此沒有感知,由於它是在瀏覽器刷新屏幕前執行的,可是咱們應該在開發中避免它,由於它會帶來必定的性能問題,咱們應該在constructor中初始化咱們的state對象,而不該該在componentDidMount調用state方法

更新階段

更新階段,當組件的props改變了,或組件內部調用了setState或者forceUpdate發生,會發生屢次

這個階段的生命週期函數調用以下:

  • componentWillReceiveProps/UNSAFE_componentWillReceiveProps
  • getDerivedStateFromProps
  • shouldComponentUpdate
  • componentWillUpdate/UNSAFE_componentWillUpdate
  • render
  • getSnapshotBeforeUpdate
  • componentDidUpdate

componentWillReceiveProps/UNSAFE_componentWillReceiveProps

componentWillReceiveProps(nextProps, prevState) UNSAFE_componentWillReceiveProps(nextProps, prevState)

在16版本這兩個方法並存,可是在17版本中componentWillReceiveProps被刪除,UNSAFE_componentWillReceiveProps,目的是爲了作向下兼容,對於新的應用,用getDerivedStateFromProps代替它們

注意,當咱們父組件從新渲染的時候,也會致使咱們的子組件調用componentWillReceiveProps/UNSAFE_componentWillReceiveProps,即便咱們的屬性和以前的同樣,因此須要咱們在這個方法裏面去進行判斷,若是先後屬性不一致纔去調用setState

在裝載階段這兩個函數不會被觸發,在組件內部調用了setState和forceUpdate也不會觸發這兩個函數

getDerivedStateFromProps

這個方法在裝載階段已經講過了,這裏再也不贅述,記住在更新階段,不管咱們接收到新的屬性,調用了setState仍是調用了forceUpdate,這個方法都會被調用

shouldComponentUpdate

shouldComponentUpdate(nextProps, nextState)

有兩個參數nextProps和nextState,表示新的屬性和變化以後的state,返回一個布爾值,true表示會觸發從新渲染,false表示不會觸發從新渲染,默認返回true

注意當咱們調用forceUpdate並不會觸發此方法

由於默認是返回true,也就是隻要接收到新的屬性和調用了setState都會觸發從新的渲染,這會帶來必定的性能問題,因此咱們須要將this.props與nextProps以及this.state與nextState進行比較來決定是否返回false,來減小從新渲染

可是官方提倡咱們使用PureComponent來減小從新渲染的次數而不是手工編寫shouldComponentUpdate代碼,具體該怎麼選擇,全憑開發者本身選擇

在將來的版本,shouldComponentUpdate返回false,仍然可能致使組件從新的渲染,這是官方本身說的

Currently, if shouldComponentUpdate() returns false, then UNSAFE_componentWillUpdate(), render(), and componentDidUpdate() will not be invoked. In the future React may treat shouldComponentUpdate() as a hint rather than a strict directive, and returning false may still result in a re-rendering of the component.

componentWillUpdate/UNSAFE_componentWillUpdate

componentWillUpdate(nextProps, nextState)
UNSAFE_componentWillUpdate(nextProps, nextState)

在16版本這兩個方法並存,可是在17版本中componentWillUpdate被刪除,UNSAFE_componentWillUpdate,目的是爲了作向下兼容

在這個方法裏,你不能調用setState,由於能走到這個方法,說明shouldComponentUpdate返回true,此時下一個state狀態已經被肯定,立刻就要執行render從新渲染了,不然會致使整個生命週期混亂,在這裏也不能請求一些網絡數據,由於在異步渲染中,可能會致使網絡請求屢次,引發一些性能問題,

若是你在這個方法裏保存了滾動位置,也是不許確的,仍是由於異步渲染的問題,若是你非要獲取滾動位置的話,請在getSnapshotBeforeUpdate調用

render

更新階段也會觸發,裝載階段已經講過了,再也不贅述

getSnapshotBeforeUpdate

getSnapshotBeforeUpdate(prevProps, prevState)

這個方法在render以後,componentDidUpdate以前調用,有兩個參數prevProps和prevState,表示以前的屬性和以前的state,這個函數有一個返回值,會做爲第三個參數傳給componentDidUpdate,若是你不想要返回值,請返回null,不寫的話控制檯會有警告

還有這個方法必定要和componentDidUpdate一塊兒使用,不然控制檯也會有警告

前面說過這個方法時用來代替componentWillUpdate/UNSAVE_componentWillUpdate,下面舉個例子說明下:

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

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // Are we adding new items to the list?
    // Capture the scroll position so we can adjust scroll later.
    if (prevProps.list.length < this.props.list.length) {
      const list = this.listRef.current;
      return list.scrollHeight - list.scrollTop;
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // If we have a snapshot value, we've just added new items. // Adjust scroll so these new items don't push the old ones out of view.
    // (snapshot here is the value returned from getSnapshotBeforeUpdate)
    if (snapshot !== null) {
      const list = this.listRef.current;
      list.scrollTop = list.scrollHeight - snapshot;
    }
  }

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

componentDidUpdate

componentDidUpdate(prevProps, prevState, snapshot)

該方法在getSnapshotBeforeUpdate方法以後被調用,有三個參數prevProps,prevState,snapshot,表示以前的props,以前的state,和snapshot。第三個參數是getSnapshotBeforeUpdate返回的

在這個函數裏咱們能夠操做DOM,和發起服務器請求,還能夠setState,可是注意必定要用if語句控制,不然會致使無限循環

卸載階段

卸載階段,當咱們的組件被卸載或者銷燬了

這個階段的生命週期函數只有一個:

  • componentWillUnmount

componentWillUnmount

當咱們的組件被卸載或者銷燬了就會調用,咱們能夠在這個函數裏去清除一些定時器,取消網絡請求,清理無效的DOM元素等垃圾清理工做

注意不要在這個函數裏去調用setState,由於組件不會從新渲染了

最後

查看 React v16.4 生命週期

大家的打賞是我寫做的動力

微信
支付寶
相關文章
相關標籤/搜索