React生命周期
每個元件都包含“生命周期方法”,你可以重寫這些方法,以便于在運作過程中特定的階段執行這些方法。
當元件執行個體被建立并插入DOM中時,其生命周期調用順序如下:
construcotr()
static getDerivedStateFromProps()
render()
componentDidMount()
當元件的props和state發生變化時會觸發更新。組建更新的生命周期調用順序如下:
shouldComponentUpdate()
getSnapshotBeforeUpdate()
componentDidUpdate()
當元件從DOM中移除時會調用如下方法:
componentWillUnmount()
當渲染過程,生命周期,或子元件的構造函數中抛出錯誤時,會調用如下方法:
static getDerivedStateFromError()
componentDidCatch()
<code>construcotr(props)</code>
如果不初始化 state 或不進行方法綁定,則不需要為 React 元件實作構造函數。
在 React 元件挂載之前,會調用它的構造函數。在為 React.Component 子類實作構造函數時,應在其他語句之前前調用 <code>super(props)</code>。否則,<code>this.props</code> 在構造函數中可能會出現未定義的 bug。
通常,在 React 中,構造函數僅用于以下兩種情況:
通過給 <code>this.state</code> 指派對象來初始化内部 state。
為事件處理函數綁定執行個體
在 <code>constructor()</code> 函數中不要調用 <code>setState()</code> 方法。如果你的元件需要使用内部 state,請直接在構造函數中為 <code>this.state</code> 指派初始 state:
隻能在構造函數中直接為 <code>this.state</code> 指派。如需在其他方法中指派,你應使用 <code>this.setState()</code> 替代。
要避免在構造函數中引入任何副作用或訂閱。如遇到此場景,請将對應的操作放置在 <code>componentDidMount</code> 中。
<code>static getDerivedStateFromProps(props, state)</code>
<code>getDerivedStateFromProps</code> 會在調用 render 方法之前調用,并且在初始挂載及後續更新時都會被調用。它應傳回一個對象來更新 state,如果傳回 null 則不更新任何内容。
此方法适用于罕見的用例,即 state 的值在任何時候都取決于 props。例如,實作 <code><Transition></code> 元件可能很友善,該元件會比較目前元件與下一元件,以決定針對哪些元件進行轉場動畫。
派生狀态會導緻代碼備援,并使元件難以維護。
如果你需要執行副作用(例如,資料提取或動畫)以響應 props 中的更改,請改用<code>componentDidUpdate</code>。
如果隻想在 prop 更改時重新計算某些資料,請使用 memoization helper 代替。
如果你想在 prop 更改時“重置”某些 state,請考慮使元件完全受控或使用 <code>key</code> 使元件完全不受控代替。
此方法無權通路元件執行個體。如果你需要,可以通過提取元件 props 的純函數及 class 之外的狀态,在<code>getDerivedStateFromProps()</code>和其他 class 方法之間重用代碼。
請注意,不管原因是什麼,都會在每次渲染前觸發此方法。這與 <code>UNSAFE_componentWillReceiveProps</code> 形成對比,後者僅在父元件重新渲染時觸發,而不是在内部調用 <code>setState</code> 時。
<code>render()</code>
<code>render()</code> 方法是 class 元件中唯一必須實作的方法。
當 <code>render</code> 被調用時,它會檢查 <code>this.props</code> 和 <code>this.state</code> 的變化并傳回以下類型之一:
React 元素。通常通過 JSX 建立。例如,<code><div /></code> 會被 React 渲染為 DOM 節點,<code><MyComponent /></code> 會被 React 渲染為自定義元件,無論是 <code><div /></code> 還是 <code><MyComponent /></code> 均為 React 元素。
數組或 fragments。 使得 render 方法可以傳回多個元素。
Portals。可以渲染子節點到不同的 DOM 子樹中。
字元串或數值類型。它們在 DOM 中會被渲染為文本節點。
布爾類型或 <code>null</code>。什麼都不渲染。(主要用于支援傳回 <code>test && <Child /></code> 的模式,其中 test 為布爾類型。)
<code>render()</code> 函數應該為純函數,這意味着在不修改元件 state 的情況下,每次調用時都傳回相同的結果,并且它不會直接與浏覽器互動。
如需與浏覽器進行互動,請在 <code>componentDidMount()</code> 或其他生命周期方法中執行你的操作。保持 <code>render()</code> 為純函數,可以使元件更容易思考。
如果 <code>shouldComponentUpdate()</code> 傳回 false,則不會調用 <code>render()</code>。
<code>componentWillUnmount()</code>
<code>componentDidMount()</code> 會在元件挂載後(插入 DOM 樹中)立即調用。依賴于 DOM 節點的初始化應該放在這裡。如需通過網絡請求擷取資料,此處是執行個體化請求的好地方。
這個方法是比較适合添加訂閱的地方。如果添加了訂閱,請不要忘記在 <code>componentWillUnmount()</code> 裡取消訂閱。
你可以在 <code>componentDidMount()</code> 裡直接調用 <code>setState()</code>。它将觸發額外渲染,但此渲染會發生在浏覽器更新螢幕之前。如此保證了即使在 <code>render()</code> 兩次調用的情況下,使用者也不會看到中間狀态。請謹慎使用該模式,因為它會導緻性能問題。通常,你應該在 <code>constructor()</code> 中初始化 state。如果你的渲染依賴于 DOM 節點的大小或位置,比如實作 modals 和 tooltips 等情況下,你可以使用此方式處理。
<code>shouldComponentUpdate(nextProps, nextState)</code>
根據 <code>shouldComponentUpdate()</code> 的傳回值,判斷 React 元件的輸出是否受目前 state 或 props 更改的影響。預設行為是 state 每次發生變化元件都會重新渲染。大部分情況下,你應該遵循預設行為。
當 props 或 state 發生變化時,<code>shouldComponentUpdate()</code> 會在渲染執行之前被調用。傳回值預設為 true。首次渲染或使用 <code>forceUpdate()</code> 時不會調用該方法。
此方法僅作為性能優化的方式而存在。不要企圖依靠此方法來“阻止”渲染,因為這可能會産生 bug。你應該考慮使用内置的 <code>PureComponent</code> 元件,而不是手動編寫 <code>shouldComponentUpdate()</code>。<code>PureComponent</code> 會對 props 和 state 進行淺層比較,并減少了跳過必要更新的可能性。
如果你一定要手動編寫此函數,可以将 <code>this.props</code> 與 <code>nextProps</code> 以及 <code>this.state</code> 與<code>nextState</code> 進行比較,并傳回 <code>false</code> 以告知 React 可以跳過更新。請注意,傳回 <code>false</code> 并不會阻止子元件在 state 更改時重新渲染。
我們不建議在 <code>shouldComponentUpdate()</code> 中進行深層比較或使用 <code>JSON.stringify()</code>。這樣非常影響效率,且會損害性能。
目前,如果 <code>shouldComponentUpdate()</code> 傳回 <code>false</code>,則不會調用 <code>UNSAFE_componentWillUpdate()</code>,<code>render()</code>和 <code>componentDidUpdate()</code>。後續版本,React 可能會将 <code>shouldComponentUpdate</code> 視為提示而不是嚴格的指令,并且,當傳回 <code>false</code> 時,仍可能導緻元件重新渲染。
<code>getSnapshotBeforeUpdate(prevProps, prevState)</code>
<code>getSnapshotBeforeUpdate()</code> 在最近一次渲染輸出(送出到 DOM 節點)之前調用。它使得元件能在發生更改之前從 DOM 中捕獲一些資訊(例如,滾動位置)。此生命周期的任何傳回值将作為參數傳遞給 <code>componentDidUpdate()</code>。
此用法并不常見,但它可能出現在 UI 進行中,如需要以特殊方式處理滾動位置的聊天線程等。
應傳回 snapshot 的值(或 <code>null</code>)。
Error boundaries是 React 元件,它會在其子元件樹中的任何位置捕獲 JavaScript 錯誤,并記錄這些錯誤,展示降級 UI 而不是崩潰的元件樹。Error boundaries 元件會捕獲在渲染期間,在生命周期方法以及其整個樹的構造函數中發生的錯誤。
如果 class 元件定義了生命周期方法 <code>static getDerivedStateFromError()</code> 或 <code>componentDidCatch()</code> 中的任何一個(或兩者),它就成為了 Error boundaries。通過生命周期更新 state 可讓元件捕獲樹中未處理的 JavaScript 錯誤并展示降級 UI。
僅使用 Error boundaries 元件來從意外異常中恢複的情況;不要将它們用于流程控制。
<code>static getDerivedStateFromError()</code>
此生命周期會在後代元件抛出錯誤後被調用。 它将抛出的錯誤作為參數,并傳回一個值以更新 state。 <code>getDerivedStateFromError()</code> 會在<code>渲染</code>階段調用,是以不允許出現副作用。 如遇此類情況,請改用 <code>componentDidCatch()</code>。
<code>componentDidCatch()</code>
此生命周期在後代元件抛出錯誤後被調用。 它接收兩個參數: <code>error</code> —— 抛出的錯誤。 <code>info</code> —— 帶有 <code>componentStack</code> key 的對象,其中包含有關元件引發錯誤的棧資訊。 <code>componentDidCatch()</code> 會在“送出”階段被調用,是以允許執行副作用。 它應該用于記錄錯誤之類的情況。
不同于上述生命周期方法(React 主動調用),以下方法是你可以在元件中調用的方法。
隻有兩個方法:<code>setState()</code> 和 <code>forceUpdate()</code>。
<code>setState(updater, [callback])</code>
<code>setState()</code> 将對元件 state 的更改排入隊列,并通知 React 需要使用更新後的 state 重新渲染此元件及其子元件。這是用于更新使用者界面以響應事件處理器和處理伺服器資料的主要方式。
将 <code>setState()</code> 視為請求而不是立即更新元件的指令。為了更好的感覺性能,React 會延遲調用它,然後通過一次傳遞更新多個元件。React 并不會保證 state 的變更會立即生效。
<code>setState()</code> 并不總是立即更新元件。它會批量推遲更新。這使得在調用 <code>setState()</code> 後立即讀取 <code>this.state</code> 成為了隐患。為了消除隐患,請使用 <code>componentDidUpdate</code> 或者 <code>setState</code> 的回調函數(<code>setState(updater, callback)</code>),這兩種方式都可以保證在應用更新後觸發。如需基于之前的 state 來設定目前的 state,請閱讀下述關于參數 <code>updater</code> 的内容。
除非 <code>shouldComponentUpdate()</code> 傳回 <code>false</code>,否則 <code>setState()</code> 将始終執行重新渲染操作。如果可變對象被使用,且無法在 <code>shouldComponentUpdate()</code> 中實作條件渲染,那麼僅在新舊狀态不一時調用 <code>setState()</code>可以避免不必要的重新渲染
參數一為帶有形式參數的 <code>updater</code> 函數:
<code>(state, props) => stateChange</code>
<code>state</code> 是對應用變化時元件狀态的引用。當然,它不應直接被修改。你應該使用基于 <code>state</code> 和 <code>props</code> 建構的新對象來表示變化。例如,假設我們想根據 <code>props.step</code> 來增加 state:
updater 函數中接收的 <code>state</code> 和 <code>props</code> 都保證為最新。updater 的傳回值會與 <code>state</code> 進行淺合并。
<code>setState()</code> 的第二個參數為可選的回調函數,它将在 <code>setState</code> 完成合并并重新渲染元件後執行。通常,我們建議使用 <code>componentDidUpdate()</code> 來代替此方式。
<code>setState()</code> 的第一個參數除了接受函數外,還可以接受對象類型:
<code>setState(stateChange[, callback])</code>
<code>stateChange</code> 會将傳入的對象淺層合并到新的 state 中,例如,調整購物車商品數:
<code>this.setState({quantity: 2})</code>
這種形式的 <code>setState()</code> 也是異步的,并且在同一周期内會對多個 <code>setState</code> 進行批處理。例如,如果在同一周期内多次設定商品數量增加,則相當于:
後調用的 <code>setState()</code> 将覆寫同一周期内先調用 <code>setState</code> 的值,是以商品數僅增加一次。如果後續狀态取決于目前狀态,我們建議使用 updater 函數的形式代替:
<code>component.forceUpdate(callback)</code>
預設情況下,當元件的 state 或 props 發生變化時,元件将重新渲染。如果 <code>render()</code> 方法依賴于其他資料,則可以調用 <code>forceUpdate()</code> 強制讓元件重新渲染。
調用 <code>forceUpdate()</code> 将緻使元件調用 <code>render()</code> 方法,此操作會跳過該元件的 <code>shouldComponentUpdate()</code>。但其子元件會觸發正常的生命周期方法,包括 <code>shouldComponentUpdate()</code> 方法。如果标記發生變化,React 仍将隻更新 DOM。
通常你應該避免使用 <code>forceUpdate()</code>,盡量在 <code>render()</code> 中使用 <code>this.props</code> 和 <code>this.state</code>。