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

React Native 中組件的生命周期

React生命周期圖

概述

每個(gè)組件都包含“生命周期方法”,你可以重寫這些方法,以便于在運(yùn)行過程中特定的階段執(zhí)行這些方法。所謂生命周期,就是一個(gè)對(duì)象從開始生成到最后消亡所經(jīng)歷的狀態(tài),理解生命周期,是合理開發(fā)的關(guān)鍵。

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

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

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

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

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

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

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

1. 掛載階段

當(dāng)組件實(shí)例被創(chuàng)建并插入 DOM 中時(shí),其生命周期調(diào)用順序如下:

1. cunstructor()

在 React 組件掛載之前,會(huì)調(diào)用它的構(gòu)造函數(shù)。在為 React.Component 子類實(shí)現(xiàn)構(gòu)造函數(shù)時(shí),應(yīng)在其他語(yǔ)句之前前調(diào)用 super(props)。否則,this.props 在構(gòu)造函數(shù)中可能會(huì)出現(xiàn)未定義的 bug。

通常,在 React 中,構(gòu)造函數(shù)僅用于以下兩種情況:

  • 通過給 this.state 賦值對(duì)象來初始化內(nèi)部 state。
  • 事件處理函數(shù)綁定實(shí)例

2. getDerivedStateFromProps() 不常用

static getDerivedStateFromProps(props, state)

getDerivedStateFromProps 會(huì)在調(diào)用 render 方法之前調(diào)用,并且在初始掛載及后續(xù)更新時(shí)都會(huì)被調(diào)用。它應(yīng)返回一個(gè)對(duì)象來更新 state,如果返回 null 則不更新任何內(nèi)容。

此方法適用于罕見的用例,即 state 的值在任何時(shí)候都取決于 props。例如,實(shí)現(xiàn) <Transition> 組件可能很方便,該組件會(huì)比較當(dāng)前組件與下一組件,以決定針對(duì)哪些組件進(jìn)行轉(zhuǎn)場(chǎng)動(dòng)畫。

派生狀態(tài)會(huì)導(dǎo)致代碼冗余,并使組件難以維護(hù)。 確保你已熟悉這些簡(jiǎn)單的替代方案:

3. render()

render()

render() 方法是 class 組件中唯一必須實(shí)現(xiàn)的方法。

當(dāng) render 被調(diào)用時(shí),它會(huì)檢查 this.propsthis.state 的變化并返回以下類型之一:

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

render() 函數(shù)應(yīng)該為純函數(shù),這意味著在不修改組件 state 的情況下,每次調(diào)用時(shí)都返回相同的結(jié)果,并且它不會(huì)直接與瀏覽器交互。

如需與瀏覽器進(jìn)行交互,請(qǐng)?jiān)?componentDidMount() 或其他生命周期方法中執(zhí)行你的操作。保持 render() 為純函數(shù),可以使組件更容易思考。

注意

如果 shouldComponentUpdate() 返回 false,則不會(huì)調(diào)用 render()。


4. componentDidMount()

componentDidMount() 會(huì)在組件掛載后(插入 DOM 樹中)立即調(diào)用。依賴于 DOM 節(jié)點(diǎn)的初始化應(yīng)該放在這里。如需通過網(wǎng)絡(luò)請(qǐng)求獲取數(shù)據(jù),此處是實(shí)例化請(qǐng)求的好地方。

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

你可以在 componentDidMount()直接調(diào)用 setState()。它將觸發(fā)額外渲染,但此渲染會(huì)發(fā)生在瀏覽器更新屏幕之前。如此保證了即使在 render() 兩次調(diào)用的情況下,用戶也不會(huì)看到中間狀態(tài)。請(qǐng)謹(jǐn)慎使用該模式,因?yàn)樗鼤?huì)導(dǎo)致性能問題。通常,你應(yīng)該在 constructor() 中初始化 state。如果你的渲染依賴于 DOM 節(jié)點(diǎn)的大小或位置,比如實(shí)現(xiàn) modals 和 tooltips 等情況下,你可以使用此方式處理

2. 更新階段

當(dāng)組件的 props 或 state 發(fā)生變化時(shí)會(huì)觸發(fā)更新。組件更新的生命周期調(diào)用順序如下:

1. getDerivedStateFromProps() 不常用

static getDerivedStateFromProps(props, state)

getDerivedStateFromProps 會(huì)在調(diào)用 render 方法之前調(diào)用,并且在初始掛載及后續(xù)更新時(shí)都會(huì)被調(diào)用。它應(yīng)返回一個(gè)對(duì)象來更新 state,如果返回 null 則不更新任何內(nèi)容。

此方法適用于罕見的用例,即 state 的值在任何時(shí)候都取決于 props。例如,實(shí)現(xiàn) <Transition> 組件可能很方便,該組件會(huì)比較當(dāng)前組件與下一組件,以決定針對(duì)哪些組件進(jìn)行轉(zhuǎn)場(chǎng)動(dòng)畫。

派生狀態(tài)會(huì)導(dǎo)致代碼冗余,并使組件難以維護(hù)。 確保你已熟悉這些簡(jiǎn)單的替代方案:

2. shouldComponentUpdate() 不常用

shouldComponentUpdate(nextProps, nextState)

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

當(dāng) props 或 state 發(fā)生變化時(shí),shouldComponentUpdate() 會(huì)在渲染執(zhí)行之前被調(diào)用。返回值默認(rèn)為 true。首次渲染或使用 forceUpdate() 時(shí)不會(huì)調(diào)用該方法。

此方法僅作為性能優(yōu)化的方式而存在。不要企圖依靠此方法來“阻止”渲染,因?yàn)檫@可能會(huì)產(chǎn)生 bug。你應(yīng)該考慮使用內(nèi)置的 PureComponent 組件,而不是手動(dòng)編寫 shouldComponentUpdate()PureComponent 會(huì)對(duì) props 和 state 進(jìn)行淺層比較,并減少了跳過必要更新的可能性。

如果你一定要手動(dòng)編寫此函數(shù),可以將 this.propsnextProps 以及 this.statenextState 進(jìn)行比較,并返回 false 以告知 React 可以跳過更新。請(qǐng)注意,返回 false 并不會(huì)阻止子組件在 state 更改時(shí)重新渲染。

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

目前,如果 shouldComponentUpdate() 返回 false,則不會(huì)調(diào)用 UNSAFE_componentWillUpdate()render()componentDidUpdate()。后續(xù)版本,React 可能會(huì)將 shouldComponentUpdate 視為提示而不是嚴(yán)格的指令,并且,當(dāng)返回 false 時(shí),仍可能導(dǎo)致組件重新渲染。

3. render()

render()

render() 方法是 class 組件中唯一必須實(shí)現(xiàn)的方法。

當(dāng) render 被調(diào)用時(shí),它會(huì)檢查 this.propsthis.state 的變化并返回以下類型之一:

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

render() 函數(shù)應(yīng)該為純函數(shù),這意味著在不修改組件 state 的情況下,每次調(diào)用時(shí)都返回相同的結(jié)果,并且它不會(huì)直接與瀏覽器交互。

如需與瀏覽器進(jìn)行交互,請(qǐng)?jiān)?componentDidMount() 或其他生命周期方法中執(zhí)行你的操作。保持 render() 為純函數(shù),可以使組件更容易思考。

注意

如果 shouldComponentUpdate() 返回 false,則不會(huì)調(diào)用 render()。

4. getSnapshotBeforeUpdate() 不常用

getSnapshotBeforeUpdate(prevProps, prevState)

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

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

應(yīng)返回 snapshot 的值(或 null)。

5. componentDidUpdate()

componentDidUpdate(prevProps, prevState, snapshot)

componentDidUpdate() 會(huì)在更新后會(huì)被立即調(diào)用。首次渲染不會(huì)執(zhí)行此方法。

當(dāng)組件更新后,可以在此處對(duì) DOM 進(jìn)行操作。如果你對(duì)更新前后的 props 進(jìn)行了比較,也可以選擇在此處進(jìn)行網(wǎng)絡(luò)請(qǐng)求。(例如,當(dāng) props 未發(fā)生變化時(shí),則不會(huì)執(zhí)行網(wǎng)絡(luò)請(qǐng)求)。

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

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

注意

如果 shouldComponentUpdate() 返回值為 false,則不會(huì)調(diào)用 componentDidUpdate()

3. 卸載階段

當(dāng)組件從 DOM 中移除時(shí)會(huì)調(diào)用如下方法:

componentWillUnmount()

componentWillUnmount() 會(huì)在組件卸載及銷毀之前直接調(diào)用。在此方法中執(zhí)行必要的清理操作,例如,清除 timer,取消網(wǎng)絡(luò)請(qǐng)求或清除在 componentDidMount() 中創(chuàng)建的訂閱等。

componentWillUnmount()不應(yīng)調(diào)用 setState(),因?yàn)樵摻M件將永遠(yuǎn)不會(huì)重新渲染。組件實(shí)例卸載后,將永遠(yuǎn)不會(huì)再掛載它。

4. 錯(cuò)誤處理

當(dāng)渲染過程,生命周期,或子組件的構(gòu)造函數(shù)中拋出錯(cuò)誤時(shí),會(huì)調(diào)用如下方法:

static getDerivedStateFromError()

static getDerivedStateFromError(error)

此生命周期會(huì)在后代組件拋出錯(cuò)誤后被調(diào)用。 它將拋出的錯(cuò)誤作為參數(shù),并返回一個(gè)值以更新 state

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

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

注意

getDerivedStateFromError() 會(huì)在渲染階段調(diào)用,因此不允許出現(xiàn)副作用。 如遇此類情況,請(qǐng)改用 componentDidCatch()。

componentDidCatch()

componentDidCatch(error, info)

此生命周期在后代組件拋出錯(cuò)誤后被調(diào)用。 它接收兩個(gè)參數(shù):

  1. error —— 拋出的錯(cuò)誤。
  2. info —— 帶有 componentStack key 的對(duì)象,其中包含有關(guān)組件引發(fā)錯(cuò)誤的棧信息。

componentDidCatch() 會(huì)在“提交”階段被調(diào)用,因此允許執(zhí)行副作用。 它應(yīng)該用于記錄錯(cuò)誤之類的情況:

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

  static getDerivedStateFromError(error) {
    // 更新 state 使下一次渲染可以顯示降級(jí) 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) {
      // 你可以渲染任何自定義的降級(jí) UI
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}

注意

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

總結(jié)

React的組件的完整的生命都介紹完了,把生命周期的回調(diào)函數(shù)總結(jié)成如下表格:

生命周期 調(diào)用次數(shù)
constructor() 1(全局調(diào)用一次)
getDerivedStateFromProps() >1
render() >=1
componentDidMount() 1
shouldComponentUpdate() >=0
getSnapshotBeforeUpdate() >=0
componentDidUpdate() >=0
componentWillUnmount() 1
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容