React生命周期中的componentWillReceiveProps和shouldComponentUpdate

好比我們人除了短暫的生與死那一瞬之外,生命中剩下的時間都用在了每天活著的狀態,對于React中的組件來講,占其總生命周期最久的就是不斷更新的狀態了。然而更新是要不斷消耗資源的,我們當然希望它能不更新就不更新同時能夠保持正常的運作。但是現在的情況是組件發生re-render是件很輕易的事,稍微改點什么東西就會導致各種程度的re-render,即便有些組件根本沒有re-render的必要,顯然這樣的情況是需要以某種方式去改善的。
所以,react提供了shouldComponentUpdate(nextProps, nextState)這個函數,此函數沒有被重寫的話默認返回true(這也就是為什么組件一言不和就re-render,因為在可能需要re-render的時候,不管最終需要不需要re-render,組件永遠re-render肯定不會出錯),但是我們可以自行重寫這個函數,讓它在某些情況下返回false即在這些情況下組件不需要re-render,只要我們有足夠把握。那我們怎樣才能變得有把握呢?在這之前我們需要知道組件何時會走生命周期中更新的那一部分流程。

組件更新的流程

  • componentWillReceiveProps(nextProps)
  • shouldComponentUpdate(nextProps, nextState)
  • componentWillUpdate()
  • render()
  • componentDidUpdate()

觸發組件更新的條件

  • 外部條件:父組件的re-render也會導致其子組件走更新流程(不管此時父組件傳給子組件的props有沒有改變),這種方式則是要走componentWillReceiveProps(nextProps)這步的,這里我們留意一下這個方法的參數nextProps
  • 內部條件:this.setState,這個自不消多說,這是組件主動更新的唯一(應該是吧?)方式,只要調用這個方法組件就會走更新的流程,但是通過這種方式觸發更新流程是不會走componentWillReceiveProps(nextProps),這一部分的,至于為什么,我也沒弄清楚,但待會兒還是會說下自己的猜想。
  • 其他條件:抱歉,剛接觸不久,暫時只想到上面兩個。
    用一張圖來總結下上面的內容
    image.png

    剛才我有說到當父組件的re-render時會導致其子組件走更新流程不管此時父組件傳給子組件的props有沒有改變,現在我們通過一個簡單的例子來證明:
//子組件
 class SubComp extends React.Component {
        constructor(props) {
          super(props)
          this.state = {
            isUpdate: false
          }
        }
        componentDidMount() {
          console.log(this.props.order + ' component Mounted')
        }
        componentWillReceiveProps(nextProps) {
          console.log(this.props.order + ' component will RecieveProps')
        }
        componentWillUpdate() {
          console.log(this.props.order + ' component will update' )
        }
        componentDidUpdate() {
          console.log(this.props.order + ' component did update' )
        }
        render() {
          console.log(this.props.order + ' component render start')
            /*接受一個從父元素傳來的props.order*/
          return(
            <button onClick={() => {this.setState({isUpdate:true})}}>
              {this.props.order+ ' button updated? : ' + this.state.isUpdate}
              <br />
              
            </button>
          )
        }
      }

    //父組件
      class SupComp extends React.Component {
        constructor(props) {
          super(props)
        }
        componentDidMount() {
          console.log('SupComponent Mounted')
        }
        componentWillReceiveProps(nextProps) {
          console.log('SupComponent will RecieveProp' )
        }
        componentWillUpdate() {
          console.log('SupComponent will update' )
        }
        componentDidUpdate() {
          console.log('SupComponent did update' )
        }
        render() {
          //渲染三個子組建,并提供一個按鈕,當按鈕被點擊時強行update
          console.log('SupComponent render start')
          return (
            <div>
              <SubComp order="1st"  />
              <br />
              <SubComp order="2nd" />
              <br />
              <SubComp order="3rd" />
              <br />
              <button onClick={() => {this.forceUpdate()}}>force update</button>
            </div>            
          )
        }
      }
      ReactDOM.render(
        <SupComp />,
        document.getElementById('example')
      )

我們先來看下外部條件觸發的情況,點擊按鈕讓父元素強行update的re-render過程中,其子元素也會走update流程

image.png

image.png

再來看下內部通過this.setState()觸發的情況,點擊子組件本身改變this.state的里的一個屬性值
image.png

好了,要證明的東西通過實例證明了,下面說兩點關于“所以然”的理解或者說猜想

componentWillReceiveProps(nextProps)shouldComponentUpdate(nextProps, nextState)兩個函數的參數的關系

顯然,在同一個更新周期內兩個nextProps是同一個東西,內容完全一樣,都是當次更新周期內父組件傳過來的props;那么,shouldComponentUpdate(nextProps, nextState)componentWillReceiveProps(nextProps)多出來的nextState哪里來的呢?或者說,為什么componentWillReceiveProps(nextProps)沒有nextState呢?先說前者,剛才我們講了兩種進入shouldComponentUpdate(nextProps, nextState)的方式,nextProps由父組件的re-render負責傳入,那nextState自然是由另外一種方式this.setState傳入,那有沒有可能nextState也由父組件的re-render傳入呢?我猜應該是沒有可能的,就我目前對react的學習來看,沒見到有人能做在父組件里面調用子組件的setState()這種操作,要如我所猜的話,就剛好解答了那上面“或者說”后面的那個問題。
好了,繼續下一個問題

子組件的this.setState()導致的update為甚么不走 componentWillReceiveProps(nextProps)的流程

我們看下程墨的《深入淺出React和Redux》(感覺很不錯的,值得入門推薦)P30對這個問題的解釋

通過this.setState方法觸發的更新過程不會調用這個函數,這是這個函數適合根據新的props值(也就是參數nextProps)來計算出是不是要更新內部狀態state。更新組件內部狀態的方法是this.setState,如果this.setState的調用導致componentWillReceiveProps(nextProps)的再一次調用,那就是一個死循環了。

上面的解釋我不大理解,我不理解里面所說的內部狀態state是不是就是子組件本身的this.state還是說是什么除了這個this.state之外組件還有什么內部對使用者透明的state。如果是前者,根據nextProps計算是不是要更新內部狀態state,怎么計算?那意味著nextPropsstate要發生關系,那就要證明存在這樣的父子組件模型,滿足父組件的re-render傳遞nextProps的同時會改變子組件的state,這個問題等價于前面“那有沒有可能nextState也由父組件的re-render傳入?”這個問題;或者起碼要證明子組件的this.setState能夠影響父組件傳來的props的,如果證明不了就說明不是this.setState不會調用componentWillReceiveProps(nextProps)這個函數,而是this.setState操作中根本無法調用這個函數或者說調用了也沒有任何意義(父元素傳過來的props沒有任何改變,走那一步有什么用)。如果是除了這個this.state之外組件還有什么內部對使用者透明的state這種情況,那還是理解不了,哎,好亂啊。

總結吧

上面一系列的問題,應該都可以歸結為一個問題:子組件update時shouldComponentUpdate(nextProps, nextState)里的兩個參數是不是分別獨立來自父組件的re-render和自身this.state?或者說每次調用shouldComponentUpdate(nextProps, nextState)nextProps===this.propsnextState===this.state兩個等式是不是必定最少有一個成立。

最后說一下開頭拋出的問題:“那我們怎樣才能變得有把握呢?”,就是,比如說父組件X傳給子組件的props組成的集合為A,子組件自身this.state為集合B,當前子組件render的內容依賴A里的一些元素也可能同時依賴B里的一些元素,前者組成的集合為a,后者為b,顯然a是A的子集,b是B子集,對于A-a和B-b那部分我們就不用管啦,因為我們不依賴,只要我們自身依賴的那些元素的值與上一次相較沒有發生變化,就可以放心大膽地(應該是這樣吧?)返回 false啦。

也不知道上面扯的錯多少,繼續學習,繼續實踐,真的是,學了不實踐幾乎等于白學,實踐才能深刻。當然,上面扯的肯定遠算不上深刻。

9.30更新

其實之前就一直有在想,我們能在componentWillReceiveProps(nextProps)這個函數里做什么呢?今天突然想到一個,可以在里面執行this.setState(),這樣的話我們就可以把UI上依賴于this.props的內容全都剝離出來統一放到this.state里,那么對于每次由于父組件re-render引起的update,我們都在componentWillReceiveProps(nextProps)將我們需要的部分this.setState(),然后shouldComponentUpdate(nextProps, nextState)里只用關注nextState的部分了,只是這樣一來this.state里的東西就多了,而且由于原來父組件傳過來的props變成自身的this.state了,而this.state可以隨意更改的,這樣是不是存在誤操作的風險。所以,我也不知道上面的做法有無意義,哈哈哈哈

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容