React 生命週期

React生命週期

基礎生命週期鉤子

  • constructor

若是你不初始化狀態,也不綁定方法,那麼你就不須要爲React組件實現構造函數。在這裏初始化狀態能夠直接對this.state賦值,在這裏使用props時,應當經過this.props使用;若是組件內定義的方法不是採用箭頭函數默認綁定到組件實例上時,調用時應該用bind手動綁定。java

  • componentWillMount(將要廢棄)

在react17版本前有效的生命週期鉤子,不能與新版鉤子混用(componentWillRecevieProps、componentWillUpdate也是如此);
有一種常見的誤區是在componentWillMount中請求數據能夠避免第一次empty render,但實際中render在componentWillMount後當即執行,若是componentWillMount拉取的數據不能當即獲得,那麼二次渲染依然沒法避免,這種狀況下推薦在componentDidMount中請求數據。
因此總的來講沒啥用處。react

  • render

render()方法是類組件惟一必須的方法,在該鉤子內,將React JSX 渲染爲DOM節點。
render()函數應該是純的,意味着不該該改變組件的狀態,其每次調用都應返回相同的結果,同時它不會直接和瀏覽器交互segmentfault

  • componentDidMount

在該鉤子內,render生成的DOM節點掛載到DOM樹中,在這裏你能夠經過ref取到DOM節點,有些需求下,可能須要DOM節點掛載後才能取到相應的屬性,例如DOM節點的尺寸等,你能夠當即調用setState()。它將會觸發一次額外的渲染,可是它將在瀏覽器刷新屏幕以前發生。這保證了在此狀況下即便render()將會調用兩次,用戶也不會看到中間狀態。瀏覽器

  • componentWillUnmount

componentWillUnmount()緊挨着在組件被卸載和銷燬以前調用。能夠在該方法裏處理任何須要的清理工做,例如解綁定時器,取消網絡請求,清理任何在componentDidMount環節建立的訂閱。服務器

clipboard.png

稍微進階的生命週期鉤子

上述介紹的基本生命週期鉤子是上圖的左半部分,走完了一次初始渲染的流程,但React組件嵌套複用,當父組件更新或者組件自身狀態更新觸發子組件從新渲染時,合理的使用上圖右半部分的生命週期鉤子能夠有效提高性能。網絡

  • componentWillReceiveProps(nextProps)(將要廢棄)

componentWillReceiveProps()在裝載了的組件接收到新屬性前調用。可能當接收到新屬性後,經過if比較新舊Props不一樣,想要經過this.setState()來更新組件的狀態。數據結構

  • shouldComponentUpdate(nextProps, nextState)

當接收到新屬性或狀態時,shouldComponentUpdate() 在渲染前被調用。默認爲true。該方法在初始化渲染或當使用forceUpdate()時並不會被調用。
適用場景是:你想在某些狀態或屬性變化時,經過this.props和nextProps以及this.state 和 nextState比較,來判斷是否須要從新渲染該組件,return fasle 不渲染,默認爲true。不能setState。
有內建的React.PureComponent代替手寫shouldComponentUpdate()。PureComponent 對屬性和狀態執行淺比較,於是下降你略過必要更新的機會。
但若是你須要更精細的控制,那就自定義shouldComponentUpdate吧。
可是當你的state或props是嵌套很深的引用類型時,容易因爲淺比較而出錯,而React文檔說咱們不推薦作深相等檢測,或使用JSON.stringify()在shouldComponentUpdate()中,這是很是無效率的會傷害性能。因此你最好設計好本身的數據結構,不要讓數據嵌套太深。架構

  • componentWillUpdate(nextProps, nextState)

相似於componentWillMount,在nextProps, nextState生效前最後的一次準備機會,能夠讀取DOM屬性,爲componentDidUpdate做準備(使用場景少)。不能setState。異步

  • componentDidUpdate

組件更新後觸發,相似於componentDidMount的做用,能夠在這裏操做DOM,發起請求等。函數

clipboard.png

參考連接:
React Lifecycle Methods- how and when to use them
React組件生命週期小結

React v16.3新出的生命週期鉤子

clipboard.png

  • static getDerivedStateFromProps(nextProps, prevState)

與舊的生命週期圖比較能夠看出,getDerivedStateFromProps處於原來的componentWillMountcomponentWillReceiveProps位置,並且this.setState()以及this.forceUpdate()也會觸發該鉤子。
該鉤子的主要做用是爲了替代componentWillReceiveProps做用。

// Before
class ExampleComponent extends React.Component {
  state = {
    isScrollingDown: false,
  };
  componentWillReceiveProps(nextProps) {
    if (this.props.currentRow !== nextProps.currentRow) {
      this.setState({
        isScrollingDown:
          nextProps.currentRow > this.props.currentRow,
      });
    }
  }
}
// After
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 to indicate no change to state.
    return null;
  }
}

與原有鉤子相比,getDerivedStateFromProps是一個靜態方法,沒法訪問組件實例,如此限制了其使用範圍,防止不熟練的開發者自由發揮致使各類bug。該鉤子主要做用能夠歸納爲接收props來改變state。

能夠發如今使用getDerivedStateFromProps時,咱們在state中多花了一個變量lastRow來保存prevProps,爲何設計鉤子的時候不在參數值加入prevProps呢? 官方文檔的解釋是:1.首次渲染也會觸發getDerivedStateFromProps,這意味着每次都須要對prevProps進行空置檢測,麻煩;2.不將prevProps傳入是爲了節省內存。

  • getSnapshotBeforeUpdate(prevProps, prevState)

取代componenWillUpdate功能。
getSnapshotBeforeUpdate在最新的渲染輸出提交給DOM前將會當即調用。它讓你的組件能在當前的值可能要改變前得到它們。這一輩子命週期返回的任何值將會 做爲參數被傳遞給componentDidUpdate()。

class ScrollingList extends React.Component {
  listRef = null;

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // Are we adding new items to the list?
    // Capture the current height of the list so we can adjust scroll later.
    if (prevProps.list.length < this.props.list.length) {
      return this.listRef.scrollHeight;
    }
    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) {
      this.listRef.scrollTop +=
        this.listRef.scrollHeight - snapshot;
    }
  }

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

  setListRef = ref => {
    this.listRef = ref;
  };
}
  • static getDerivedStateFromError()componentDidCatch()

錯誤處理。。。
render phase 裏產生異常的時候, 會調用 getDerivedStateFromError;

在 commit phase 裏產生異常大的時候, 會調用 componentDidCatch。

componentDidCatch 是不會在服務器端渲染的時候被調用的 而getDerivedStateFromError 會

爲什麼用推出新的鉤子,廢棄舊的鉤子?

主要是爲了適應將要推出的異步渲染,其次是避免以前生命週期鉤子的濫用。

同步渲染與異步渲染

React Fiber 是在 v16 的時候引入的一個全新架構, 旨在解決異步渲染問題,但v16並無開啓。
同步渲染的痛點:當應用的組件樹特別龐大時,因爲javaScript是單線程的,從新渲染一旦開始,中間不會停,若是這時候用戶去操做, 好比輸入, 點擊按鈕, 此時頁面是沒有響應的。 等更新完了, 你以前的那些輸入就會啪啪啪一會兒出來了。函數調用棧如圖所示:

clipboard.png

由於JavaScript單線程的特色,每一個同步任務不能耗時太長,否則就會讓程序不會對其餘輸入做出相應,React的更新過程就是犯了這個禁忌,而React Fiber就是要改變現狀。

Fiber 的作法是:分片。
把一個很耗時的任務分紅不少小片,每個小片的運行時間很短,雖然總時間依然很長,可是在每一個小片執行完以後,都給其餘任務一個執行的機會,這樣惟一的線程就不會被獨佔,其餘任務依然有運行的機會。 而維護每個分片的數據結構, 就是Fiber。
用一張圖來展現Fiber 的碎片化更新過程

clipboard.png

在React Fiber中,一次更新過程會分紅多個分片完成,因此徹底有可能一個更新任務尚未完成,就被另外一個更高優先級的更新過程打斷,這時候,優先級高的更新任務會優先處理完,而低優先級更新任務所作的工做則會徹底做廢,而後等待機會重頭再來。

由於一個更新過程可能被打斷,因此React Fiber一個更新過程被分爲兩個階段: render phase and commit phase.(能夠從上文的生命週期圖中看到)

由於第一階段的過程會被打斷並且「重頭再來」,就會形成意想不到的狀況。

好比說,一個低優先級的任務A正在執行,已經調用了某個組件的componentWillUpdate函數,接下來發現本身的時間分片已經用完了,因而冒出水面,看看有沒有緊急任務,哎呀,真的有一個緊急任務B,接下來React Fiber就會去執行這個緊急任務B,任務A雖然進行了一半,可是沒辦法,只能徹底放棄,等到任務B全搞定以後,任務A重頭來一遍,注意,是重頭來一遍,不是從剛纔中段的部分開始,也就是說,componentWillUpdate函數會被再調用一次。

在現有的React中,每一個生命週期函數在一個加載或者更新過程當中絕對只會被調用一次;在React Fiber中,再也不是這樣了,第一階段中的生命週期函數在一次加載和更新過程當中可能會被屢次調用!。

總而言之就是:
render phase 能夠被打斷, 你們不要在此階段作一些有反作用的操做,能夠放心在commit phase 裏作。

而後就是生命週期的調整, react 把你有可能在render phase 裏作的有反作用的函數都改爲了static 函數, 強迫開發者作一些純函數的操做。

全面瞭解 React 新功能: Suspense 和 Hooks

相關文章
相關標籤/搜索