淺析 React 生命週期

Overview

最近常有學習React相關的技術,寫了幾個React的小Demo,使用 React/Express 技術棧。實在過小,羞於拿出來細說。React 的確是一個值得追隨的技術。但React體系實在龐大,我目前僅略知一二。這裏要挑出來講的,是React的生命週期機制。Demo的學習過程當中,對它的方便、易用之處實在是深有體會,在一些細節處也值得斟酌,在這裏作一下記錄,便於分享。javascript

若是你接觸過React,大概對rendercomponentWillMount等,會相對的熟悉,由於它們再經常使用不過。但用歸用,其中的一些理論上的細節,每每容易在使用的過程當中被忽略,使咱們多敲了很多代碼,心很累的 : )html

通俗來說,React 將組件 component 在web中的造成、修改和渲染等劃分爲若干個階段,組成組件的生命週期。在一個完整的生命週期內,一個組件會通過若干個階段,在特殊的階段組件會調用一個特別的lifecycle method,即生命週期方法。以下:java

  1. constructor(props)react

  2. componentWillMount()ios

  3. render()git

  4. componentDidMount()github

  5. componentWillReceiveProps(nextProps)web

  6. shouldComponentUpdate(nextProps, nextState)編程

  7. componentWillUpdate(nextProps, nextState)axios

  8. render( )* //理解上與3. render()略有不一樣,見下。

  9. componentDidUpdate(prevProps, prevState )

  10. componentWillUnmount( )

值得注意,這些生命週期是React 內置的,在特定條件下就會被調用。而開發者能夠作的就是 override(重載)這些方法,以實現想要的功能。

constructor

constructor(props),組件造成時調用。

constructor 函數可理解爲組件的構造函數,從組件的類(class) 實例化一個組件實例。這個函數在組件造成時被調用,是全部生命週期函數中最早執行的。在constructor函數內,若有必要,進行state的初始化以及綁定方法;不然能夠省去constructor函數的聲明。

有如下幾點在開發時值得注意:

  1. constructor 函數內,在執行任何statement以前,必須是super() 函數,若是有參數須將參數帶上。這點跟Java很像。

  2. 在constructor 函數內,this.props 返回 undefined

  3. 不要在初試化state時引用props 裏的值,不然每當props更新時,都須要在componentWillReceiveProps 函數內對state進行更新。(同時這也涉及到組件state選取的原則,若有須要請閱讀Thinking in React

class App extends Component {
  constructor(props) {
    super(props);//------------(1)
    console.log(this.props);// undefined ------------(2)
    //initialize the state
    this.state = {
      value: '',
      color: props.initialColor  // 不可取  ------------(3)
    }
    //bind methods
    this.handleClick = this.handleClick.bind(this);
  }
}

componentWillMount

componentWillMount(),在組件首次渲染(render)以前調用。

mount安裝之意,咱們能夠理解爲組件首次被加載在web中。所以每次頁面加載/刷新,或者某個組件第一次加載進入web時能夠調用componentWillMount( ) 函數。舉個例子,在首次進入文章列表時時,可在 componentWillMount 對全部文章進行查詢。這樣,在render以前,就能拿到全部文章的數據,以便在render中使用。

在componentWillMount ( ) 函數內,若對this.state進行更新,沒法觸發從新渲染組件。

class PostList extends Component {
  //...
  //在componentWillMount 組件內獲取全部博客列表
  componentWillMount(){
    axios.get('/posts')
         .then(res=>{
           //...
         });
  }
  //在 render 函數內將拿到的博客列表 渲染在頁面中
  render(){
    //...
  }
}

Render

render()

render 即 渲染函數,是編寫組件代碼時,惟一一個必須的函數。該函數須有返回值,返回一個組件,即最終渲染出來的組件。在使用組件的class進行組件實例化時,獲得的即是其返回值。

返回值有兩種類型:

  1. 一個父標籤,這個父標籤內能夠包含若干個子標籤,在最外層標籤必須只有一個。

  2. false 或者 null,表明不渲染任何DOM

class App extends Component {
  //...
  render(){
    return (
      <div>
          //...
      </div>
    )
  }
}

注意:在render函數中只作與返回組件相關的工做,勿在其中對state進行操做,能夠保證每次調用render函數,返回的組件都是相同的。不然將加大項目維護成本。

另外,若是shouldComponentUpdate函數返回false,則不執行render函數。關於shouldComponentUpdate將在下面介紹。

componentDidMount

componentDidMount(),一旦組件首次加載完成,便會調用

若是須要對渲染出來的DOM節點作任何操做,能夠在此處進行。(提示: this.refs 可獲取真實DOM)。

在該組件內設置state將會致使組件被從新渲染。

class App extends Component {
  //..
  componentDidMount(){
    //將會觸發組件從新渲染
    this.setState({
      value: '100'
    }):
    //對節點進行操做
      this.refs.div.appendChild(newChild);
  }
  
}

上面對 React生命週期函數中的constructor / componentWillMount / render / componentDidMount 四個函數進行了介紹。下面將繼續介紹另外5個方法。在此以前,先總結一下,下面列表中列出的3.render()8.render()的在邏輯上的區別和聯繫。先上一個列表。

  1. constructor(props)

  2. componentWillMount( )

  3. render( )

  4. componentDidMount( )

  5. componentWillReceiveProps(nextProps)

  6. shouldComponentUpdate(nextProps, nextState)

  7. componentWillUpdate(nextProps, nextState)

  8. render()*

  9. componentDidUpdate(prevProps, prevState)

  10. componentWillUnmount()

「兩個」render( )方法的區別

3.render( ) 與 8.render( )*

實質上,這兩個方法毫無區別。但這裏爲何要說起它們之間的區別呢?其實,它們只是同一函數 render( ) 在組件生命週期的兩個不一樣階段的不一樣理解而已。

前一個 render( ) 方法指在組件第一次被加載進入頁面時,調用的 render( ) 方法;後一個則指除去第一次,以後調用的 render( ) 方法。

所以,咱們更願意稱第一次的 render( ) 方法爲 mount( 安裝 ),稱後一個 render( ) 方法爲 re-render ( 從新渲染 ) 。這也是爲何組件首次 render 先後的方法名中帶有mount一詞的緣故了。

這是 React 的伎倆,或者設計哲學吧。怎麼認爲都行,我認爲頗有趣?

下面介紹的方法,都是圍繞第二個 render( ) ,即從新渲染 re-render 展開的。

componentWillReceiveProps

componentWillReceiveProps(nextprops)已加載的組件在 props 發生變化時調用。

若是須要經過監聽 props 的改變來修改 state 的值,則能夠經過重載該函數實現。

須要注意,在有些狀況下,組件的 props 未發生改變也會調用該函數。所以若是在該函數內的邏輯,只是想捕獲當前 props 與 接收的 nextProps 的不一樣來作出一些操做,則最好先將 props 與 nextProps 進行比較。

1.在mounting階段,即首次 render ,不調用 componentWillReceiveProps 方法。理解了兩個 render( ) 的不一樣,便知道這裏是爲何了。

2.this.setState({…}) 不觸發 componentWillReceiveProps 方法。由於該方法只監聽 this.props 的改變,不關心 this.state 值的變化。

class App extends Component {
  componentWillReceiveProps(nextProps){
    //接收的顏色 與 當前顏色不一樣時
    if (this.props.color !== nextProps.color){
      ...
    }
  }
}

shouldComponentUpdate

shouldComponentUpdate(nextProps, nextState)

返回 true orfalse

要不要更新(從新渲染)組件?淺顯易懂。這個方法的返回值決定了,當 props 或者 state 值發生變化時,組件是否從新渲染。兩種狀況:

  1. 返回true,從新渲染。緊接着,繼續執行 componentWillUpdate()render()componentDidUpdate()

  2. false,不從新渲染。再也不執行任何生命週期函數函數(亦不執行該組件的 render( ) 函數)。可是,並不妨礙其子組件。也就是說,若是其子組件的 props 或 state 發生改變時,只會取決於那個組件的 shouleComponentUpdate ( ) 方法的返回值。道理雖懂,但遇到是可能會犯迷糊,由於開發中經常會碰見組件嵌套的狀況,父子組件之間傳遞同一套 props 或 state,一來二去,誰更新誰不更新,容易迷糊,須要仔細咯。

  1. 在絕大部分狀況下,當 props 或 state 改變時,都是須要從新渲染組件的。

  2. 注意,根據 React 官方 的說法,就算 shouldComponentUpdate( ) 方法返回 false,組件也會從新渲染。須要隨時注意官方文檔的變化。

class PostList extends Component {
  shouldComponentUpdate(nextProps, nextState){
    //return true;默認
    return false;// 不更新組件
  }
}

componentWillUpdate

componentWillUpdate(nextProps, nextState),當 shouldComponentUpdate( ) 方法返回 true 後調用。

這個方法提供了一個爲從新渲染做準備的機會,意思是要在這裏,趁接下來的 render( ) 方法從新渲染以前,完成該完成的操做。這個方法在 mount 階段不會被調用,只在 re-render 階段被調用。

注意,不要在該方法內調用 this.setState({…}),若有須要,請在 componentWillReceiveProps( ) 方法中完成。養成良好的編程規範。

class App extends Component {
  componentWillUpdate(nextProps, nextState){
    var isLate = this.nextProps.isLate;
    if(isLate){
      //...
    } else {
      //...
    }
  }
}

componentDidUpdate

componentDidUpdate(prevProps, preState),一旦組件首次更新(從新渲染)完成時調用。

所以像 componentDidMount( ) 同樣,若是須要對渲染出來的DOM節點作任何操做,能夠在此處進行。(提示: this.refs 可獲取真實DOM)。

在該組件內設置state將會致使組件被從新渲染。

class App extends Component {
  //..
  componentDidUpdate(){
    //將會觸發組件從新渲染
    this.setState({
      value: '100'
    });
    //對節點進行操做
    this.refs.div.appendChild(newChild);
  }
  
}

componentWillUnmount

componentWillUnmount(),在組件即將被卸載(或銷燬)以前調用。

在這個方法中,適合作一些清理善後工做。例如清楚timer,取消網絡請求,或者清除在 componentDidMount 或 componentDidUpdate 中生成的相關 DOM 節點。

總結

  1. mountre-render 的是有區別的。

  2. mount階段使用前一部分的四個方法( constructor / componentWillMount / render / componentDidMount),圍繞組件首次加載而調用;

  3. 後一部分 re-render 相關的,使用 componentWillReceiveProps / shouldComponentUpdate / componentWillUpdate / render / componentDidUpdate ,圍繞組件從新渲染而調用。

我總結了一張流程圖和一個表格,以表示這些周期函數之間的關係,以及在何種狀況下會調用這些函數。

注意:componentWillUnmount 方法未包含其中。

lifecycle

mount props 變化 state 變化
constructor componentWillReceiveProps shouldComponentUpdate
componentWillMount shouldComponentUpdate (return true) ⏬ / 結束
render (return true) ⏬ / 結束 componentWillUpdate
componentDidMount componentWillUpdate render
/ render componentDidUpdate
/ componentDidUpdate /

完。

文章爲本人原創,原文見本人個博:
淺析「React」生命週期(一)
淺析「React」生命週期(二)

相關文章
相關標籤/搜索