React中組件的生命周期(最新版本的生命周期)

React Native 中組件的生命周期

React生命周期圖

概述

每個組件都包含“生命周期方法”,你可以重寫這些方法,以便于在運行過程中特定的階段執行這些方法。所謂生命周期,就是一個對象從開始生成到最后消亡所經歷的狀態,理解生命周期,是合理開發的關鍵。

可以把組件生命周期大致分為三個階段:

  • 第一階段:Mounting(掛載階段)

    是組件第一次繪制階段,在這里完成了組件的加載和初始化;

  • 第二階段:Updating(更新階段)

    是組件在運行和交互階段,這個階段組件可以處理用戶交互,或者接收事件更新界面;

  • 第三階段:Unmounting(卸載階段)

    是組件卸載消亡的階段,這里做一些組件的清理工作。

1. 掛載階段

當組件實例被創建并插入 DOM 中時,其生命周期調用順序如下:

1. cunstructor()

在 React 組件掛載之前,會調用它的構造函數。在為 React.Component 子類實現構造函數時,應在其他語句之前前調用 super(props)。否則,this.props 在構造函數中可能會出現未定義的 bug。

通常,在 React 中,構造函數僅用于以下兩種情況:

  • 通過給 this.state 賦值對象來初始化內部 state
  • 事件處理函數綁定實例

2. getDerivedStateFromProps() 不常用

static getDerivedStateFromProps(props, state)

getDerivedStateFromProps 會在調用 render 方法之前調用,并且在初始掛載及后續更新時都會被調用。它應返回一個對象來更新 state,如果返回 null 則不更新任何內容。

此方法適用于罕見的用例,即 state 的值在任何時候都取決于 props。例如,實現 <Transition> 組件可能很方便,該組件會比較當前組件與下一組件,以決定針對哪些組件進行轉場動畫。

派生狀態會導致代碼冗余,并使組件難以維護。 確保你已熟悉這些簡單的替代方案:

3. render()

render()

render() 方法是 class 組件中唯一必須實現的方法。

render 被調用時,它會檢查 this.propsthis.state 的變化并返回以下類型之一:

  • React 元素。通常通過 JSX 創建。例如,<div /> 會被 React 渲染為 DOM 節點,<MyComponent /> 會被 React 渲染為自定義組件,無論是 <div /> 還是 <MyComponent /> 均為 React 元素。
  • 數組或 fragments。 使得 render 方法可以返回多個元素。欲了解更多詳細信息,請參閱 fragments 文檔。
  • Portals。可以渲染子節點到不同的 DOM 子樹中。欲了解更多詳細信息,請參閱有關 portals 的文檔
  • 字符串或數值類型。它們在 DOM 中會被渲染為文本節點
  • 布爾類型或 null。什么都不渲染。(主要用于支持返回 test && <Child /> 的模式,其中 test 為布爾類型。)

render() 函數應該為純函數,這意味著在不修改組件 state 的情況下,每次調用時都返回相同的結果,并且它不會直接與瀏覽器交互。

如需與瀏覽器進行交互,請在 componentDidMount() 或其他生命周期方法中執行你的操作。保持 render() 為純函數,可以使組件更容易思考。

注意

如果 shouldComponentUpdate() 返回 false,則不會調用 render()


4. componentDidMount()

componentDidMount() 會在組件掛載后(插入 DOM 樹中)立即調用。依賴于 DOM 節點的初始化應該放在這里。如需通過網絡請求獲取數據,此處是實例化請求的好地方。

這個方法是比較適合添加訂閱的地方。如果添加了訂閱,請不要忘記在 componentWillUnmount() 里取消訂閱

你可以在 componentDidMount()直接調用 setState()。它將觸發額外渲染,但此渲染會發生在瀏覽器更新屏幕之前。如此保證了即使在 render() 兩次調用的情況下,用戶也不會看到中間狀態。請謹慎使用該模式,因為它會導致性能問題。通常,你應該在 constructor() 中初始化 state。如果你的渲染依賴于 DOM 節點的大小或位置,比如實現 modals 和 tooltips 等情況下,你可以使用此方式處理

2. 更新階段

當組件的 props 或 state 發生變化時會觸發更新。組件更新的生命周期調用順序如下:

1. getDerivedStateFromProps() 不常用

static getDerivedStateFromProps(props, state)

getDerivedStateFromProps 會在調用 render 方法之前調用,并且在初始掛載及后續更新時都會被調用。它應返回一個對象來更新 state,如果返回 null 則不更新任何內容。

此方法適用于罕見的用例,即 state 的值在任何時候都取決于 props。例如,實現 <Transition> 組件可能很方便,該組件會比較當前組件與下一組件,以決定針對哪些組件進行轉場動畫。

派生狀態會導致代碼冗余,并使組件難以維護。 確保你已熟悉這些簡單的替代方案:

2. shouldComponentUpdate() 不常用

shouldComponentUpdate(nextProps, nextState)

根據 shouldComponentUpdate() 的返回值,判斷 React 組件的輸出是否受當前 state 或 props 更改的影響。默認行為是 state 每次發生變化組件都會重新渲染。大部分情況下,你應該遵循默認行為。

當 props 或 state 發生變化時,shouldComponentUpdate() 會在渲染執行之前被調用。返回值默認為 true。首次渲染或使用 forceUpdate() 時不會調用該方法。

此方法僅作為性能優化的方式而存在。不要企圖依靠此方法來“阻止”渲染,因為這可能會產生 bug。你應該考慮使用內置的 PureComponent 組件,而不是手動編寫 shouldComponentUpdate()PureComponent 會對 props 和 state 進行淺層比較,并減少了跳過必要更新的可能性。

如果你一定要手動編寫此函數,可以將 this.propsnextProps 以及 this.statenextState 進行比較,并返回 false 以告知 React 可以跳過更新。請注意,返回 false 并不會阻止子組件在 state 更改時重新渲染。

我們不建議在 shouldComponentUpdate() 中進行深層比較或使用 JSON.stringify()。這樣非常影響效率,且會損害性能。

目前,如果 shouldComponentUpdate() 返回 false,則不會調用 UNSAFE_componentWillUpdate()render()componentDidUpdate()。后續版本,React 可能會將 shouldComponentUpdate 視為提示而不是嚴格的指令,并且,當返回 false 時,仍可能導致組件重新渲染。

3. render()

render()

render() 方法是 class 組件中唯一必須實現的方法。

render 被調用時,它會檢查 this.propsthis.state 的變化并返回以下類型之一:

  • React 元素。通常通過 JSX 創建。例如,<div /> 會被 React 渲染為 DOM 節點,<MyComponent /> 會被 React 渲染為自定義組件,無論是 <div /> 還是 <MyComponent /> 均為 React 元素。
  • 數組或 fragments。 使得 render 方法可以返回多個元素。欲了解更多詳細信息,請參閱 fragments 文檔。
  • Portals。可以渲染子節點到不同的 DOM 子樹中。欲了解更多詳細信息,請參閱有關 portals 的文檔
  • 字符串或數值類型。它們在 DOM 中會被渲染為文本節點
  • 布爾類型或 null。什么都不渲染。(主要用于支持返回 test && <Child /> 的模式,其中 test 為布爾類型。)

render() 函數應該為純函數,這意味著在不修改組件 state 的情況下,每次調用時都返回相同的結果,并且它不會直接與瀏覽器交互。

如需與瀏覽器進行交互,請在 componentDidMount() 或其他生命周期方法中執行你的操作。保持 render() 為純函數,可以使組件更容易思考。

注意

如果 shouldComponentUpdate() 返回 false,則不會調用 render()

4. getSnapshotBeforeUpdate() 不常用

getSnapshotBeforeUpdate(prevProps, prevState)

getSnapshotBeforeUpdate() 在最近一次渲染輸出(提交到 DOM 節點)之前調用。它使得組件能在發生更改之前從 DOM 中捕獲一些信息(例如,滾動位置)。此生命周期的任何返回值將作為參數傳遞給 componentDidUpdate()

此用法并不常見,但它可能出現在 UI 處理中,如需要以特殊方式處理滾動位置的聊天線程等。

應返回 snapshot 的值(或 null)。

5. componentDidUpdate()

componentDidUpdate(prevProps, prevState, snapshot)

componentDidUpdate() 會在更新后會被立即調用。首次渲染不會執行此方法。

當組件更新后,可以在此處對 DOM 進行操作。如果你對更新前后的 props 進行了比較,也可以選擇在此處進行網絡請求。(例如,當 props 未發生變化時,則不會執行網絡請求)。

你也可以在 componentDidUpdate()直接調用 setState(),但請注意它必須被包裹在一個條件語句里,正如上述的例子那樣進行處理,否則會導致死循環。它還會導致額外的重新渲染,雖然用戶不可見,但會影響組件性能。不要將 props “鏡像”給 state,請考慮直接使用 props。 欲了解更多有關內容,請參閱為什么 props 復制給 state 會產生 bug

如果組件實現了 getSnapshotBeforeUpdate() 生命周期(不常用),則它的返回值將作為 componentDidUpdate() 的第三個參數 “snapshot” 參數傳遞。否則此參數將為 undefined。

注意

如果 shouldComponentUpdate() 返回值為 false,則不會調用 componentDidUpdate()

3. 卸載階段

當組件從 DOM 中移除時會調用如下方法:

componentWillUnmount()

componentWillUnmount() 會在組件卸載及銷毀之前直接調用。在此方法中執行必要的清理操作,例如,清除 timer,取消網絡請求或清除在 componentDidMount() 中創建的訂閱等。

componentWillUnmount()不應調用 setState(),因為該組件將永遠不會重新渲染。組件實例卸載后,將永遠不會再掛載它。

4. 錯誤處理

當渲染過程,生命周期,或子組件的構造函數中拋出錯誤時,會調用如下方法:

static getDerivedStateFromError()

static getDerivedStateFromError(error)

此生命周期會在后代組件拋出錯誤后被調用。 它將拋出的錯誤作為參數,并返回一個值以更新 state

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {    // 更新 state 使下一次渲染可以顯降級 UI    return { hasError: true };  }
  render() {
    if (this.state.hasError) {      // 你可以渲染任何自定義的降級  UI      return <h1>Something went wrong.</h1>;    }
    return this.props.children; 
  }
}

注意

getDerivedStateFromError() 會在渲染階段調用,因此不允許出現副作用。 如遇此類情況,請改用 componentDidCatch()

componentDidCatch()

componentDidCatch(error, info)

此生命周期在后代組件拋出錯誤后被調用。 它接收兩個參數:

  1. error —— 拋出的錯誤。
  2. info —— 帶有 componentStack key 的對象,其中包含有關組件引發錯誤的棧信息

componentDidCatch() 會在“提交”階段被調用,因此允許執行副作用。 它應該用于記錄錯誤之類的情況:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // 更新 state 使下一次渲染可以顯示降級 UI
    return { hasError: true };
  }

  componentDidCatch(error, info) {    // "組件堆棧" 例子:    //   in ComponentThatThrows (created by App)    //   in ErrorBoundary (created by App)    //   in div (created by App)    //   in App    logComponentStackToMyService(info.componentStack);  }
  render() {
    if (this.state.hasError) {
      // 你可以渲染任何自定義的降級 UI
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}

注意

如果發生錯誤,你可以通過調用 setState 使用 componentDidCatch() 渲染降級 UI,但在未來的版本中將不推薦這樣做。 可以使用靜態 getDerivedStateFromError() 來處理降級渲染。

總結

React的組件的完整的生命都介紹完了,把生命周期的回調函數總結成如下表格:

生命周期 調用次數
constructor() 1(全局調用一次)
getDerivedStateFromProps() >1
render() >=1
componentDidMount() 1
shouldComponentUpdate() >=0
getSnapshotBeforeUpdate() >=0
componentDidUpdate() >=0
componentWillUnmount() 1
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容