React 從 v16 開始,像是跨入了新的時代,性能和新的 API 都使人矚目。從新認識 React,從從新認識生命週期開始。爲了更好的支持異步渲染(Async Rendering),解決一些生命週期濫用可能致使的問題,React 從 V16.3
開始,對生命週期進行漸進式調整,同時在官方文檔也提供了使用的最佳實踐。前端這裏咱們將簡要對比 React 新舊生命週期,從新認識一下 React 生命週期。java
getDerivedStateFromProps()
getSnapshotBeforeUpdate()
react
componentWillMount()
componentWillReceiveProps()
componentWillUpdate()
ajax
雖然廢棄了這三個生命週期方法,可是爲了向下兼容,將會作漸進式調整。(詳情見#12028)
V16.3 並未刪除這三個生命週期,同時還爲它們新增以 UNSAFE_
前綴爲別名的三個函數 UNSAFE_componentWillMount()
、UNSAFE_componentWillReceiveProps()
、UNSAFE_componentWillUpdate()
。數組
在 16.4 版本給出警告將會棄用 componentWillMount()
、componentWillReceiveProps()
、componentWillUpdate()
三個函數服務器
而後在 17 版本將會刪除 componentWillMount()
、componentWillReceiveProps()
、componentWillUpdate()
這三個函數,會保留使用 UNSAFE_componentWillMount()
、UNSAFE_componentWillReceiveProps()
、UNSAFE_componentWillUpdate()
網絡
圖示數據結構
react的生命週期大概分爲框架
不能進行修改state的操做
,即便作了,也不會進行新數據狀態的渲染。在該函數中作的操做,均可以提早到構造函數中。渲染函數
,惟一的必定不能省略的函數,必須有返回值,返回null或false表示不渲染任何DOM元素。它是一個僅僅用於渲染的純函數,返回值徹底取決於this.state和this.props,不能在函數中任何修改props、state、拉取數據等具備反作用的操做。render函數返回的是JSX的對象,該函數並不由於這渲染到DOM樹,什麼時候進行真正的渲染是有React庫決定的。(setState是一個異步函數)掛載成功函數
。該函數不會再render函數調用完成以後當即調用,由於render函數僅僅是返回了JSX的對象,並無當即掛載到DOM樹上,而componentDidMount是在組件被渲染到DOM樹以後被調用的。另外,componentDidMount函數在進行服務器端渲染時不會被調用。當組件掛載到DOM樹上以後,props/state
被修改會致使組件進行更新操做。更新過程會以此調用以下的生命週期函數:dom
卸載過程只涉及一個函數componentWillUnmount,當React組件要從DOM樹上刪除前,會調用一次這個函數。這個函數常常用於去除componentDidMount函數帶來的反作用,例如清除計時器
、刪除componentDidMount中創造的非React元素
。
setState
要修改state,只能使用this.setState()
,不能使用this.state.value='myData' 相似方式設置state,一是不會驅動從新渲染,二是極可能被後面的操做替換,形成沒法預知的錯誤。此外,React利用狀態隊列來實現setState的異步更新,避免頻繁地重複更新state
。當同時作了不少setState操做的時候,react會智能的合併成一個setState,當須要肯定的setState完成後的操做,可使用
setState({}, () => { // 在這裏進行state改變後的操做 })
setState的調用是有風險的,在某些生命週期函數中調用可能會無用甚至早恆循環調用致使崩潰。state的初始化通常在構造函數中實現;setState能夠在裝載過程的componentWillMount、componentDidMount中調用
;setState能夠在更新過程當中的componentWillReceiveProps、componentDidUpdate中調用
render
render是一個異步函數,render執行後並不會直接生成Dom,而是生成虛擬Dom節點(模擬HTML Dom節點的一個javaScript數據結構),什麼時候生成真實的DOM樹取決於react框架自己的計算
圖示
getDerivedStateFromProps
static getDerivedStateFromProps(nextProps, prevState) { //根據nextProps和prevState計算出預期的狀態改變,返回結果會被送給setState }
getSnapshotBeforeUpdate
差別
全部被刪除的生命週期函數,目前還湊合着用,可是隻要用了,開發模式下會有紅色警告,在下一個大版本(也就是React v17)更新時會完全廢棄。
static getDerivedStateFromProps(nextProps, prevState) { 4. Updating state based on props 7. Fetching external data when props change } constructor() { 1. Initializing state } componentDidMount() { 2. Fetching external data 3. Adding event listeners (or subscriptions) } shouldComponentUpdate() { } render() { } getSnapshotBeforeUpdate(prevProps, prevState) { 8. Reading DOM properties before an update } componentDidUpdate(prevProps, prevState, snapshot) { 5. Invoking external callbacks 6. Side effects on props change } componentWillUnmount() { }
// before
componentWillMount() { // 1. Initializing state // 2. Fetching external data // 3. Adding event listeners (or subscriptions) } componentWillReceiveProps() { // 4. Updating state based on props // 6. Side effects on props change // 7. Fetching external data when props change } componentWillUpdate(nextProps, nextState) { // 5. Invoking external callbacks // 8. Reading DOM properties before an update }
目前 react 16.8 +的生命週期分爲三個階段,分別是 掛載階段、 更新階段、 卸載階段
掛載階段:constructor(props)
: 實例化。static getDeriverdStateFromProps
從 props
中獲取 state
。render
渲染。componentDidMount
: 完成掛載。
更新階段:static getDeriverdStateFromProps
從 props
中獲取 state
。shouldComponentUpdate
判斷是否須要重繪。render
渲染。getSnapshotBeforeUpdate
獲取快照。componentDidUpdate
渲染完成後回調。
卸載階段:componentWillUnmount
即將卸載。
錯誤處理:static getDerivedStateFromError
從錯誤中獲取 state。componentDidCatch
捕獲錯誤並進行處理。
組件實例建立並插入 DOM 時,按順序調用如下方法:
constructor()
static getDerivedStateFromProps()
componentWillMount()/UNSAFE_componentWillMount()(being deprecated)render()
componentDidMount()
有定義 getDerivedStateFromProps 時,會忽略 componentWillMount()/UNSAFE_componentWillMount()
(詳情查看源碼)
1)constructor()constructor(props)
構造函數一般用於:
注意:ES6 子類的構造函數必須執行一次 super()。React 若是構造函數中要使用 this.props,必須先執行 super(props)。
2)static getDerivedStateFromProps()static getDerivedStateFromProps(nextProps, prevState)
當建立時、接收新的 props 時、setState 時、forceUpdate 時會執行這個方法。
注意:v16.3 setState 時、forceUpdate 時不會執行這個方法,v16.4 修復了這個問題。
這是一個靜態方法
,參數 nextProps
是新接收的 props
,prevState
是當前的 state
。返回值(對象)將用於更新 state,若是不須要更新則須要返回 null。
下面是官方文檔給出的例子
class ExampleComponent extends React.Component { // Initialize state in constructor, // Or with a property initializer. state = { isScrollingDown: false, lastRow: null, }; static getDerivedStateFromProps(props, state) { if (props.currentRow !== state.lastRow) { return { isScrollingDown: props.currentRow > state.lastRow, lastRow: props.currentRow, }; } // Return null to indicate no change to state. return null; } }
這個方法的經常使用做用也很明顯了:父組件傳入新的 props
時,用來和當前的 state
對比,判斷是否須要更新 state
。之前通常使用 componentWillReceiveProps
作這個操做。
這個方法在建議儘可能少用
,只在必要的場景中使用,通常使用場景以下:
無條件的根據 props 更新 state
當 props 和 state 的不匹配狀況更新 state
詳情能夠參考官方文檔的最佳實踐 You Probably Don’t Need Derived State
3)componentWillMount()/UNSAFE_componentWillMount()(棄用)UNSAFE_componentWillMount()
這個方法已經不推薦使用。由於在將來異步渲染機制下,該方法可能會屢次調用。它所行使的功能也能夠由 componentDidMount() 和 constructor() 代替:
以前有些人會把異步請求放在這個生命週期,其實大部分狀況下都推薦把異步數據請求放在 componentDidMount() 中
。在服務端渲染時,一般使用 componentWillMount() 獲取必要的同步數據
,可是可使用 constructor() 代替它。
可使用 setState,不會觸發 re-render
4)renderrender()
每一個類組件中,render() 惟一必須的方法。
render() 正如其名,做爲渲染用,能夠返回下面幾種類型:
注意:
Arrays 和 String 是 v16.0.0 新增。
fragments 是 v16.2.0 新增。
Portals 是 V16.0.0 新增。
裏面不該該包含反作用,應該做爲純函數。
不能使用 setState。
5)componentDidMount()componentDidMount()
組件完成裝載(已經插入 DOM 樹)時,觸發該方法。這個階段已經獲取到真實的 DOM。
通常用於下面的場景:
componentWillReceiveProps()/UNSAFE_componentWillReceiveProps()(being deprecated)static getDerivedStateFromProps()
shouldComponentUpdate()
componentWillUpdate()/UNSAFE_componentWillUpdate()(being deprecated)render()
getSnapshotBeforeUpdate()
componentDidUpdate()
有 getDerivedStateFromProps
或者 getSnapshotBeforeUpdate
時,componentWillReceiveProps()
/UNSAFE_componentWillReceiveProps()
和 componentWillUpdate()
/UNSAFE_componentWillUpdate()
不會執行 (詳情查看源碼)
1)componentWillReceiveProps()/UNSAFE_componentWillReceiveProps()(棄用)UNSAFE_componentWillReceiveProps(nextProps)
這個方法在接收新的 props 時觸發,即便 props 沒有變化也會觸發。
通常用這個方法來判斷 props 的先後變化來更新 state,以下面的例子:
class ExampleComponent extends React.Component { state = { isScrollingDown: false, }; componentWillReceiveProps(nextProps) { if (this.props.currentRow !== nextProps.currentRow) { this.setState({ isScrollingDown: nextProps.currentRow > this.props.currentRow, }); } } }
這個方法將被棄用,推薦使用 getDerivedStateFromProps
代替。
可使用 setState
2)static getDerivedStateFromProps()
同 Mounting 時所述一致。
3)shouldComponentUpdate()
在接收新的 props 或新的 state 時,在渲染前會觸發該方法。
該方法經過返回 true 或者 false 來肯定是否須要觸發新的渲染。返回 false, 則不會觸發後續的 UNSAFE_componentWillUpdate()、render() 和 componentDidUpdate()(可是 state 變化仍是可能引發子組件從新渲染)。
因此一般經過這個方法對 props 和 state 作比較,從而避免一些沒必要要的渲染。
PureComponent 的原理就是對 props 和 state 進行淺對比(shallow comparison),來判斷是否觸發渲染。
4)componentWillUpdate()/UNSAFE_componentWillUpdate() (棄用)UNSAFE_componentWillUpdate(nextProps, nextState)
當接收到新的 props 或 state 時,在渲染前執行該方法。
在之後異步渲染時,可能會出現某些組件暫緩更新,致使 componentWillUpdate 和 componentDidUpdate 之間的時間變長,這個過程當中可能發生一些變化,好比用戶行爲致使 DOM 發生了新的變化,這時在 componentWillUpdate 獲取的信息可能就不可靠了。
不能使用 setState
5)render()
同 Mounting 時所述一致。
6)getSnapshotBeforeUpdate()getSnapShotBeforeUpdate(prevProps, prevState)
這個方法在 render() 以後,componentDidUpdate() 以前調用。
兩個參數 prevProps 表示更新前的 props,prevState 表示更新前的 state。
返回值稱爲一個快照(snapshot),若是不須要 snapshot,則必須顯示的返回 null —— 由於返回值將做爲 componentDidUpdate() 的第三個參數使用。因此這個函數必需要配合 componentDidUpdate() 一塊兒使用。
這個函數的做用是在真實 DOM 更新(componentDidUpdate)前,獲取一些須要的信息(相似快照功能),而後做爲參數傳給 componentDidUpdate。例如:在 getSnapShotBeforeUpdate 中獲取滾動位置,而後做爲參數傳給 componentDidUpdate,就能夠直接在渲染真實的 DOM 時就滾動到須要的位置。
下面是官方文檔給出的例子:
class ScrollingList extends React.Component { constructor(props) { super(props); this.listRef = React.createRef(); } getSnapshotBeforeUpdate(prevProps, prevState) { // Are we adding new items to the list? // Capture the scroll position so we can adjust scroll later. if (prevProps.list.length < this.props.list.length) { const list = this.listRef.current; return list.scrollHeight - list.scrollTop; } return null; } componentDidUpdate(prevProps, prevState, snapshot) { // If we have a snapshot value, we've just added new items. // Adjust scroll so these new items don't push the old ones out of view. // (snapshot here is the value returned from getSnapshotBeforeUpdate) if (snapshot !== null) { const list = this.listRef.current; list.scrollTop = list.scrollHeight - snapshot; } } render() { return ( <div ref={this.listRef}>{/* ...contents... */}</div> ); } }
7)componentDidUpdate()componentDidUpdate(prevProps, prevState, snapshot)
這個方法是在更新完成以後調用,第三個參數 snapshot 就是 getSnapshotBeforeUpdate 的返回值。
正如前面所說,有 getSnapshotBeforeUpdate 時,必需要有 componentDidUpdate。因此這個方法的一個應用場景就是上面看到的例子,配合 getSnapshotBeforeUpdate 使用。
可使用 setState,會觸發 re-render,因此要注意判斷,避免致使死循環。
1)componentWillUnmount()
componentWillUnmount()
在組件卸載或者銷燬前調用。這個方法主要用來作一些清理工做,例如:
不能使用 setState
componentDidCatch()componentDidCatch(err, info)
任何子組件在渲染期間,生命週期方法中或者構造函數 constructor 發生錯誤時調用。
錯誤邊界不會捕獲下面的錯誤:
總結
React 生命週期能夠查看 生命週期圖
雖然 React 有作向下兼容,可是推薦儘可能避免使用廢棄的生命週期,而是擁抱將來,用新的生命週期替換它們。
若是你不想升級 React,可是想用新的生命週期方法,也是能夠的。使用 react-lifecycles-compat polyfill,能夠爲低版本的 React(0.14.9+)提供新的生命週期方法。
若是你以爲這篇文章對你有所幫助,那就順便點個贊
吧,點贊收藏
不迷路~
黑芝麻哇,白芝麻發,黑芝麻白芝麻哇發哈!
前端哇發哈