理解React-元件生命週期

frank26發表於2018-03-03

作者-Bartosz Szczeciński原文

React提供給開發者很多方法或‘鉤子’,呼叫於一個元件的生命週期,使我們更新UI和app state。瞭解何時使用哪些對正確理解如何使用React至關重要。

constructor

constuctors是OOP的基礎——一個無論何時一個新object被建立時被呼叫的特殊函式。呼叫一個特殊函式super非常重要,在我們的class extent任何其它class(也有定義了constructor)時。呼叫此特殊函式將call 我們parent class的constructor且允許初始化其本身。此即為何我們只有在初始化地call了super時才有許可權訪問this.props的原因。

如上所述,constructor對於構建我們元件——建立任何領域(以this.開始的變數)或者初始化以接收到的props為基礎的state——非常完美。

這也是你通過在直接覆蓋this.state之處期待change/set state僅有的欄位。在別的其它例項上記得用this.setState.

DO

  • set initial state
  • 若未用class屬性語法-準備所有class 欄位和bind 將作為callback傳遞的函式。

DON'T

  • cause any side effects (AJAX calls etc.)

componentWillMount

這是一個特殊情況 - componentWillMount與建構函式沒有多大區別 - 它只在初始安裝生命週期中被呼叫一次。 從歷史上看,使用componentWillMountover建構函式有一些原因,請參閱此react-redux問題,但請記住,此處描述的練習已被棄用。

很多人會試圖使用這個函式來傳送請求來獲取資料,並希望在初始渲染準備好之前資料可用。 情況並非如此 - 當請求在渲染之前被初始化時,在渲染被呼叫之前它將不能完成。

此外,隨著對React Fiber的更改(post React 16 beta版本),此函式最終可能會在初始渲染被呼叫之前呼叫多次,因此可能會導致觸發多重副作用。 由於這個事實,不推薦使用這個函式來產生任何副作用。

值得注意的是,在使用伺服器端渲染時呼叫此函式,而在這種情況下,它不會在伺服器上呼叫對應的componentDidMount,而是在客戶端上呼叫。 所以如果在伺服器部分需要一些副作用,這個函式應該被用作例外。

此函式中使用的setState是“空閒”的,不會觸發重新渲染。

DO

  • update state 通過 this.setState
  • perform last minute optimization(執行最後一分鐘優化)
  • 僅在SSR造成side-effects (AJAX calls etc.)

DON'T

  • 不在CS造成side-effects (AJAX calls etc.)

componentWillReceiveProps(nextProps)

這個函式將在每個更新生命週期中由props變化(父元素重新渲染)呼叫,並且將傳遞所有props傳遞的物件圖,無論自上次重新渲染後props值是否已經改變亦或自前個渲染階段。

這個函式是理想的,如果你有一個元件的狀態部分取決於從父元件傳遞的props作為呼叫this.setState在這裡不會導致額外的渲染呼叫。

請記住,由於所有props都呼叫了該函式,所以即使對那些沒有改變的props,開發人員也會執行檢查以確定實際值是否發生了變化,例如:

componentWillReceiveProps(nextProps) {
  if(nextProps.myProp !== this.props.myProps) {
    // nextProps.myProp has a different value than our current prop
    // so we can perform some calculations based on the new value
  }
}
複製程式碼

由於使用React Fiber(16階測試版)這個功能可能會在實際呼叫renderfunction之前多次呼叫,因此不建議在此使用任何副作用導致的操作。

DO

  • sync state to props(同步化state到props)

DON'T

  • 造成side-effects (AJAX calls etc.)

shouldComponentUpdate(nextProps, nextState, nextContext)

預設情況下,所有基於類的元件都會在它們接收到的props,其狀態或環境發生變化時重新render自己。 如果重新渲染元件是計算重啟(例如生成圖表)或者出於某些效能原因不推薦使用,則開發人員可以訪問將在update cycle中呼叫的特殊函式。

這個函式將在內部呼叫props,state和object的下一個值。 開發人員可以使用它們來驗證更改是否需要重新渲染,並返回false以防止重新渲染髮生。 在其他情況下,您預計會返回true。

DO

  • 用於提高perfomance

DON'T

  • 造成side-effects (AJAX calls etc.)
  • call this.setState

componentWillUpdate(nextProps, nextState)

如果shouldComponentUpdate函式未被使用,或者它決定元件在此渲染週期中應更新,則將呼叫另一個生命週期函式。 此函式通常用於在state的某些部分基於props時,執行狀態和props同步。

在實現shouldComponentUpdate的情況下,可以使用此函式而不是componentWillReceiveProps,因為只有在實際重新渲染元件時才會呼叫此函式。

與所有其他componentWill *函式類似,此函式可能在渲染前最終呼叫多次,因此不建議在此執行導致操作的副作用。 DO

  • synchronize state to props

DON'T

  • 造成side-effects (AJAX calls etc.)

componentDidUpdate(prevProps, prevState, prevContext)

這個函式將在渲染完成後在每個重新渲染週期中被呼叫。 這意味著您可以確定元件及其所有子元件已正確render其自身。

由於這是唯一保證在每個重新render週期中僅被呼叫一次的函式,因此建議將此函式用於任何導致操作的副作用。 與componentWillUpdate和componentWillReceiveProps類似,即使這些值沒有發生實際更改,也會使用以前的props,state和context的object對映來呼叫此函式。 因為開發人員需要手動檢查給定的值是否發生了變化,然後才能執行各種更新操作:

componentDidUpdate(prevProps) {
  if(prevProps.myProps !== this.props.myProp) {
    // this.props.myProp has a different value
    // we can perform any operations that would 
    // need the new value and/or cause side-effects 
    // like AJAX calls with the new value - this.props.myProp
  }
}
複製程式碼

DO

  • 造成side-effects (AJAX calls etc.)

DON'T

  • call this.setState 因其可致re-render

上述規則的一個例外是基於某些DOM屬性更新狀態,只有在元件重新呈現後才能計算該屬性(例如,某些DOM節點的位置/維度)。 請注意防止更新,如果值實際上沒有更改,可能會導致渲染迴圈。

componentDidCatch(errorString, errorInfo)

React 16中的新增功能 - 此生命週期方法的特殊之處在於它可以對發生在子元件中的事件作出反應,特別是對任何子元件中發生的任何未捕獲錯誤。

通過這個新增,你可以讓你的父母元素處理錯誤 - 例如 - 設定錯誤資訊狀態並在其渲染中返回適當的訊息,或者登入到報告系統,例如:

componentDidCatch(errorString, errorInfo) {
  this.setState({
    error: errorString
  });
  ErrorLoggingTool.log(errorInfo);
}
render() {
  if(this.state.error) return <ShowErrorMessage error={this.state.error} />
  return (
    // render normal component output
  );
}
複製程式碼

發生錯誤時,該函式將被呼叫:

  • errorString - 錯誤的.toString()訊息
  • errorInfo - 具有單個欄位componentStack的物件,它表示堆疊跟蹤返回錯誤發生的位置,例如:
in Thrower
    in div (created by App)
    in App

複製程式碼

componentDidMount

這個函式在給定元件的整個生命週期中只會被呼叫一次,並且它被呼叫表示元件及其所有子元件被正確渲染。

由於這個函式被保證只被呼叫一次,因此它是完成AJAX請求等引起副作用的操作的理想選擇。

DO

  • 造成side-effects (AJAX calls etc.)

DON'T

  • call this.setState 因其可致re-render

上述規則的一個例外是基於某些DOM屬性更新狀態,只有在元件重新呈現後才能計算該屬性(例如,某些DOM節點的位置/維度)。 請注意防止更新,如果值實際上沒有更改,可能會導致渲染迴圈。

componentWillUnmount

如果它使用定時器(setTimeout,setInterval),開啟套接字或執行我們需要在不再需要時關閉/移除的任何操作,則使用此函式在元件後面“清除”。

DO

  • remove any timers or listeners created in lifespan of the component

DON'T

  • call this.setState, start new listeners or timers

Component cycles

元件可能re-render有多種原因,並且每個元件中都會呼叫不同的函式,以便開發人員更新元件的某些部分。

  • Component creation(元件建立)

第一個迴圈是元件的建立,這通常是在解析的JSX?中第一次遇到元件時發生的:

理解React-元件生命週期

  • Component re-rendering due to re-rendering of the parent component(由於父元件更新而元件更新)

理解React-元件生命週期

  • Component re-rendering due to internal change (e.g. a call to this.setState())( 由於內部改變的元件更新)

理解React-元件生命週期

  • Component re-rendering due to call to this.forceUpdate(由於call this.forceUpdate而re-render的元件)

理解React-元件生命週期

  • Component re-rendering due to catching an error(由於捕獲錯誤而更新元件)

在React 16中引入了“ErrorBoundaries”。 一個元件可以定義一個特殊的層,它可以捕獲錯誤並提供一個新的生命週期方法 - componentDidCatch - 它允許開發人員提供自我修復操作以恢復或優雅地處理錯誤。

理解React-元件生命週期

Shoutout to@James_k_nelson,他剛剛釋出了一個componentWillReceiveProps模擬器,你可以在reactarmory.com/guides/life…找到並使用它。

相關文章