一、組件生命周期
每個react類組件都包含“生命周期方法”(function組件是沒有生命周期方法的),你可以重寫這些方法,以便於在運行過程中特定的階段執行這些方法。在接下來的文章中,生命周期方法的書寫會遵照如下規則,以方便大家理解。
1、加粗生命周期方法表示常用的生命周期方法,開發中會經常使用到。
2、未加粗的生命周期方法表示罕見的生命周期方法,開發中很少會用到。
3、畫中橫線的生命周期方法表示即將過期的方法,開發中應避免使用。
4、若一定要使用即將失效的生命周期方法,可以參考官方文檔。
首先通過一幅圖來看下react組建的全部生命周期(version: ^16.4,之前的版本會略有區別)。
二、掛載
當組件實例被創建並插入 DOM 中時,被稱為組件掛載,其生命周期調用順序如下:
-
constructor()
static getDerivedStateFromProps()
UNSAFE_componentWillMount()
render()
componentDidMount()
1. constructor(props)
在 React 組件掛載之前,會調用它的構造函數。在為 React.Component 子類實現構造函數時,應在其他語句之前前調用super(props)。否則,this.props在構造函數中可能會出現未定義的 bug。
通常,在 React 中,構造函數僅用於以下兩種情況:
- 通過給this.state賦值對象來初始化內部 state。
- 為事件處理函數綁定實例
如果不初始化 state 且不進行方法綁定,則不需要為 React 組件實現構造函數。
2. static getDerivedStateFromProps(props, state)
getDerivedStateFromProps會在調用 render 方法之前調用,並且在初始掛載及后續更新時都會被調用。它應返回一個對象來更新 state,如果返回 null 則不更新任何內容。此方法無權訪問組件實例。如果你需要,可以通過提取組件 props 的純函數及 class 之外的狀態,在getDerivedStateFromProps()和其他 class 方法之間重用代碼。
3. render()
render()方法是 class 組件中唯一必須實現的方法。該函數應該為純函數,這意味着在不修改組件 state 的情況下,每次調用時都返回相同的結果,並且它不會直接與瀏覽器交互。
如需與瀏覽器進行交互,請在componentDidMount()或其他生命周期方法中執行你的操作。保持render()為純函數,可以使組件更容易思考。
4. componentDidMount()
componentDidMount()會在組件掛載后(插入 DOM 樹中)立即調用。依賴於 DOM 節點的初始化應該放在這里。如需通過網絡請求獲取數據,此處是實例化請求的好地方。
這個方法是比較適合添加訂閱的地方。如果添加了訂閱,請不要忘記在componentWillUnmount()里取消訂閱。
三、更新
當組件的 props 或 state 發生變化時會觸發更新。組件更新的生命周期調用順序如下:
-
static getDerivedStateFromProps()
shouldComponentUpdate()
UNSAFE_componentWillReceiveProps()
UNSAFE_componentWillUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
1、shouldComponentUpdate(nextProps, nextState)
該方法是控制組件是否應該被更新。如果方法返回false則后續的生命周期方法將全部被跳過,該方法會接收nextProps 和nextState,可以通過比較this.props 和nextProps、this.state和nextState來決定是否要重新渲染組件,來對組件進行性能優化。你應該考慮使用內置的 PureComponent 組件,而不是手動編寫 shouldComponentUpdate()
官網中不建議在 shouldComponentUpdate() 中進行深層比較或使用 jsON.stringify()。這樣非常影響效率,且會損害性能。
2、getSnapshotBeforeUpdate(prevProps, prevState)
getSnapshotBeforeUpdate() 在最近一次渲染輸出(提交到 DOM 節點)之前調用。它使得組件能在發生更改之前從 DOM 中捕獲一些信息(例如,滾動位置)。此生命周期的任何返回值將作為參數傳遞給 componentDidUpdate()。
3、componentDidUpdate(prevProps, prevState, snapshot)
componentDidUpdate() 會在更新后會被立即調用。首次渲染不會執行此方法。
當組件更新后,可以在此處對 DOM 進行操作。如果你對更新前后的 props 進行了比較,也可以選擇在此處進行網絡請求。(例如,當 props 未發生變化時,則不會執行網絡請求)
componentDidUpdate(prevProps) {
// 典型用法(不要忘記比較 props): if (this.props.userID !== prevProps.userID) { this.fetchData(this.props.userID); } }
你也可以在 componentDidUpdate() 中直接調用 setState(),但請注意它必須被包裹在一個條件語句里,正如上述的例子那樣進行處理,否則會導致死循環。
四、卸載
componentWillUnmount()
componentWillUnmount()會在組件卸載及銷毀之前直接調用。在此方法中執行必要的清理操作,例如,清除 timer,取消網絡請求或清除在componentDidMount()中創建的訂閱等。
五、錯誤處理
static getDerivedStateFromError()
static getDerivedStateFromError(error) { // 更新 state 使下一次渲染可以顯示降級 UI return { hasError: true }; }
此生命周期會在后代組件拋出錯誤后被調用。 它將拋出的錯誤作為參數,並返回一個值以更新 state,你可以通過判斷state中的某個字段,來顯示降級UI
componentDidCatch()
此生命周期在后代組件拋出錯誤后被調用,可以用於日志記錄。 它接收兩個參數:
- error—— 拋出的錯誤。
- info—— 帶有componentStack key 的對象
componentDidCatch(error, info) {
logToMyService(info.componentStack);
}