當前版本v16.8.4 裝載過程(組件第一次在DOM樹中渲染的過程): constructor(常用) getInitialState(v16.0已廢棄) getDefaultProps(v16.0已廢棄) componentWillMount(v17.0中將被棄用) getDerivedState ...
當前版本v16.8.4
裝載過程(組件第一次在DOM樹中渲染的過程):
constructor(常用) -> getInitialState(v16.0已廢棄) -> getDefaultProps(v16.0已廢棄) -> componentWillMount(v17.0中將被棄用) -> getDerivedStateFromProps(v16.3新增,併在v16.4中升級優化了一下) -> render(必須要) -> componentDidMount(常用)
更新過程(當組件被重新渲染的過程,state改變或props改變或父組件forceUpdate引發子組件的重新渲染):
componentWillReceiveProps(v17.0中將被棄用) -> getDerivedStateFromProps -> shouldComponentUpdate -> componentWillUpdate(v17.0中將被棄用) -> render -> getSnapshotBeforeUpdate -> componentDidUpdate
卸載過程(組件從DOM中刪除的過程):
componentWillUnmount
錯誤處理(當組件發生錯誤的時候,用得極少)
getDerivedStateFromError(v16.6新增) -> componentDidCatch(未來將被廢棄)
constructor:(可以不寫,直接寫state = {})
ES6中每個類的構造函數,並不是每個組件都需要定義自己的構造函數。比如無狀態組件。
作用:
- 初始化state, 例如super(props)下的 this.state = {}
- 綁定成員函數的this環境。例如 this.onClickButton = this.onClickButton.bind(this)。但這種方案一般都會用箭頭函數代替
而綜合以上兩點作用,其實很多時候,你會在antd官網的例子上看到一些例子,並沒有使用contructor, 而是直接簡寫為
state = {
count: 0
}
// 這是因為在ES6的繼承中,其實不管子類寫不寫constructor,在new實例的過程都會給補上constructor
class ColorPoint extends Point {
}
// 等同於
class ColorPoint extends Point {
constructor(...args) {
super(...args);
}
}
getInitialState: (隨著v16.0版本createClass被棄用,該方法也不存在了)
返回值會用來初始化組件的this.state
getDefaultProps: (隨著v16.0版本createClass被棄用,該方法也不存在了)
返回值可以作為props的初始值
componentWillMount:(即將在v17.0中被棄用,不用)
可以做:
- 直接使用setState改變組件狀態,render會列印一次改變後的值(但是你這樣做沒什麼意義,還不如直接在constructor的時候設好初始值)
- 發送Ajax請求(服務端渲染的時候)
不要做:
- 發送ajax請求(原因如下)
- 執行DOM操作(這個階段DOM還沒渲染出來)
最終建議:
不要用這個生命周期
在沒被棄用的時候也幾乎不用,這時候沒有任何渲染出來的結果,即使調用this.setState修改狀態也不會引發重新繪製。所有在這裡可以做的事,都可以提前到 constructor中去做。有些人可能用過vue, 在vue中也經常在created中去請求介面,比如可能初始值是0,然後在created中請求介面,簡單的理解成想頁面在展示的時候就直接顯示介面請求返回後的數據1了,而不是我們看到頁面的時候先看到0,然後突然變成1了。個人理解vue的created和react的componentWillMount應該也是相差不了太多的,如果是在componentWillMount的時候你的數據還不是1的話,你這時候請求數據,其實是另外開了一個線程去執行非同步操作了,render函數並不會等你非同步請求結果返回1才去執行render。網路差的話,你先看到0再看到突然變成1也是很正常的事。在這裡請求和在componentDidMount中請求並不會有太大的差別。同理,其實vue中特意區分該在created中還是mounted中請求介面也是沒必要的,還不如統一到mounted/componentDidMount中去請求介面,因為我們有些方法還是要等真實dom存在後才去執行的。
getDerivedStateFromProps(props, state):
第一次在裝載階段(當前組件實例化)會被觸發比較好理解,但是組件更新階段,究竟組件更新階段的什麼操作會觸發這個函數?
假如我在父組件改變了props 會觸發這個函數嗎?答案是會。
假如我在當前組件 this.setState 會觸發這個函數嗎?在v16.3中不會,但是在v16.4以上就會了。截至2019-03-17,在 https://react.docschina.org/docs/react-component.html#static-getderivedstatefromprops 上看到的翻譯還是錯的。不信可以自己試試?
假如我在父組件只是執行了 forceUpdate 強行引發一次重新繪製,那當前組件(子組件)的getDerivedStateFromProps又會被觸發嗎?
一樣,在v16.3中不會,但是在v16.4以上就會了。
它返回一個對象來更新狀態,如果返回的是null就表示不更新任何內容
這個方法react官方都意識到很多人對如何使用它存在許多誤解(https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html),
目前很多人的博客其實寫得也有點問題的。
render:(必須要)
返回一個JSX表示的對象,然後由React庫來根據返回對象決定如何渲染。並不是返回的真實DOM,其他的生命周期都可以省,但是這個必須要
componentDidMount:(常用)
可以做:
- 發送ajax請求
不要做:
- 直接使用this.setState更新狀態,這樣會二次渲染(不過經常會有發送請求後,在回調函數里setState,這也是不可避免的)
僅在瀏覽器端執行,此時已經有了真實的dom節點,在這個階段常用於處理介面請求,或者一些DOM操作。因為有些組件的啟動動作是依賴DOM的,例如動畫等。
需要註意的是:可以在這裡發送非同步請求,在回調函數里調用setState設置state。但是儘量不要在這裡直接調用setState()設置狀態。因為會觸發一次額外的重新渲染,可能造成性能問題。
componentWillReceiveProps(nextProps):(即將在v17.0中被棄用,不用)
可以做:
- 根據props的更新同步組件狀態。
不要做:
- 發送ajax請求
- 尤其是不要在這裡setState
當前組件setState()不會調用;
父組件props改變會調用;
父組件通過forceUpdate重新執行render,也會調用當前組件的componentWillReceiveProps;
只要父組件的render函數被調用,那麼當前組件(子組件)都會觸發這個函數,不管父組件的props發不發生變化。例如父組件執行了 forceUpdate 強行引發一次重新繪製。當前組件的componentWillReceiveProps就會被觸發。而在當前組件this.setState是不會觸發這個函數的。因為componentWillReceiveProps適合根據新的props值(也就是參數nextProps)來計算出是不是要更新內部狀態state。更新組件內部狀態的方法就是this.setState。如果this.setState的調用導致componentWillReceiveProps再依次被調用,那就是一個死迴圈了。
componentWillReceiveProps(nextProps) {
if (this.props.oneProp !== nextProps.oneProp) {
// 很多人會在這裡同時寫setState 並且 發送非同步請求,其實這是不合理的
// 應該只在這裡根據props的改變去執行 setState來同步props到組件的狀態中, 不該在這裡發送非同步請求
}
}
// 現如今更好的方式是使用getDerivedStateFromProps 去改變state 和 componentDidUpdate 去非同步請求 來代替上面的方案
shouldComponentUpdate(nextProps, nextState):(如果你對性能有更極致的要求,水平沒達到一定高度就不要去動它了)
當前組件setState()會調用;
父組件props改變會調用;
父組件通過forceUpdate重新執行render,不會調用當前組件的shouldComponentUpdate;
這個方法主要是為了性能優化而設計的,考慮使用內置的PureComponent,而不是自己在這個函數里寫比較,更不建議在這個組件里使用JSON.stringify去深度檢查,效率非常低。
這裡返回一個布爾值,預設都返回的true, 如果返回false,那這個生命周期後面的更新階段的生命周期都不會執行了。
componentWillUpdate(nextProps, nextState):(即將在v17.0中被棄用,不用)
它可以做的,不要做的同componentWillMount一樣
當前組件setState()會調用;
父組件props改變會調用;
父組件通過forceUpdate重新執行render,也會調用當前組件的shouldComponentUpdate;
其實和componentWillMount類似,
這個方法也應該儘量避免使用,將要被遺棄。幾乎用不上。應該都統一到componentDidUpdate中去處理。
getSnapshotBeforeUpdate(prevProps, prevState):
setState(),props發生改變,父組件重新render都會調用。發生在更新狀態的render之後,這時候已經可以讀取dom了。通常用於處理滾動位置的聊天線程等UI中。
和getDerivedStateFromProps一樣它返回一個對象來更新狀態,如果返回的是null就表示不更新任何內容
componentDidUpdate(prevProps, prevState):
它可以做的,不要做的同componentDidUpdate一樣
setState(),props發生改變,父組件重新render都會調用。這個方法相對用得也比較多。同componentDidMount處理事件函數類似,如果組件被更新的時候,原有的內容被重新繪製後可能也需要再次處理事件函數。
componentDidUpdate(prevProps, prevState) {
if(prevProps.oneProp !== this.props.oneProp) {
// 如果有變化,在這裡做一些非同步請求,可能會在非同步請求的回調函數里setState
// 不要直接去執行this.setState
}
}
componentWillUnmount():
當react組件要從dom樹上刪除掉的時候,這個方法就會被調用。
如果在componentDidMount中使用非React的方法創造了一些DOM元素,如果撒手不管可能會造成記憶體泄漏,那就需要在componentWillUnmount中把這些創造的DOM元素清理掉。
應用場景:清理定時器,關閉socket, 清除監聽器等
import React, { Component } from "react";
export default class LifeCycle extends Component {
state = {
}
static getDerivedStateFromProps(props, state) {
}
componentDidMount = () => {
}
shouldComponentUpdate = (nextProps, nextState) => {
}
getSnapshotBeforeUpdate = (prevProps, prevState) => {
}
componentDidUpdate = (prevProps, prevState) => {
}
componentWillUnmount = () => {
}
render() {
return (
<div>
</div>
)
}
}