關於react生命週期的一些我的理解(其中包括一些其餘帖子的轉載)

一、老版本的生命週期以及按順序執行的流程
Initialization (初始化階段)
這些方法會在組件初始化的時候被調用,只跟實例的建立有關。react

若是用 createReactClass 進行建立,則還有 getInitialState 這些生命週期函數,但不多使用,咱們這裏不說起。算法

static defaultProps{} (getDefaultProps())
定義默認 props ,會和父組件傳下來的 props 進行合併,且以父組件中的 props 優先級更高,至關於 {...defaultProps, props} 。redux

static propTypes{} (getInitialState())
定義 props 的數據類型,能夠幫助咱們肯定其有效性,減小許多開發中沒必要要的錯誤。瀏覽器

constructor()
在加載階段前調用一次,進行 state 的初始化。性能優化

constructor(props){服務器

super(props)

}網絡

super(props) 用來調用父類的構建方法。數據結構

Mounting(加載階段)
componentWillMount()
新版中爲 UNSAFE_componentWillMount() 。架構

只在組件加載時調用,整個生命週期只調用一次,之後組件更新也不會調用,此時能夠修改 state。dom

render()
react 中最重要的生命週期函數,建立虛擬 dom,並進行 diff 算法,更新 dom 樹也在此進行。因此不該該在此改變組件的狀態或者與瀏覽器進行交互。

注意:這個函數不能缺乏,若是不建立虛擬 dom,能夠 return null 。

componentDidMount()
組件加載完成後當即調用,整個生命週期只調用一次,能夠獲取到更新後的 DOM,在此處能夠進行網絡請求等。

Updating(更新階段)
componentWillReceiveProps()
新版中爲 UNSAFE_componentWillReceiveProps() 。

在組件加載後,若是有新的 props 傳遞過來,會先調用這個函數,能夠在這裏調用 setState() 修改 state 。

componentWillReceiveProps(nextProps)

shouldComponentUpdate()
react 中一個重要的性能優化點,組件接收到新的 props 或者 state ,返回 true 表示須要更新 dom,返回 false 阻止更新。

shouldComponentUpdate(nextProps, nextState)

componentWillUpdate()
新版中爲 UNSAFE_componentWillUpdate() 。

組件加載時不調用,只有在組件須要更新(即 shouldComponentUpdate 返回 true )時調用。

componentWillUpdate(nextProps, nextState)

注意:不能在這個方法中調用 setState() 修改 state 。

不然會從新觸發updating

render()
componentDidUpdate()
在組件更新完成後當即被調用,能夠進行網絡請求等。

componentDidUpdate(prevProps, prevState)

Unmouting(銷燬階段)
componentWillUnmount()
在組件被卸載和銷燬以前調用,能夠在這裏處理任何須要的清理工做,好比解除定時器,取消已經發起的網絡請求,清理在 componentDidMount 函數中建立的 DOM 元素。

componentWillUnmount()

Catching(捕獲階段)
componentDidCatch()
錯誤邊界捕獲,在v16.0剛推出的時候新增長的一個生命週期函數,用於捕獲在 子組件樹 中任意地方發生的JavaScript 錯誤,一個錯誤邊界不能捕獲它本身內部的錯誤。

componentDidCatch(error, info)

二、新版本中新增的生命週期以及對應的使用方法
首先,新版本中有三個生命週期被deprecate(去掉)了,他們分別是:

componentWillReceiveProps
componentWillMount
componentWillUpdate
getDerivedStateFromProps()

這個getDerivedStateFromProps是一個靜態函數,因此函數體內不能訪問this,簡單說,就是應該一個純函數,純函數的話,輸出徹底由輸入決定。

static getDerivedStateFromProps(nextProps, prevState) {

      //根據nextProps和prevState計算出預期的狀態改變,返回結果會被送給setState

}

每當父組件引起當前組件的渲染過程時,getDerivedStateFromProps會被調用,這樣咱們有一個機會能夠根據新的props和以前的state來調整新的state,若是放在三個被deprecate生命週期函數中實現比較純,沒有反作用的話,基本上搬到getDerivedStateFromProps裏就好了;

注意:getDerivedStateFromProps不管是Mounting仍是Updating,也不管是由於什麼引發的Updating,所有都會被調用。

getSnapshotBeforeUpdate()

這函數會在render以後執行,而執行之時DOM元素尚未被更新,給了一個機會去獲取DOM信息,計算獲得一個snapshot,這個snapshot會做爲componentDidUpdate的第三個參數傳入。

getSnapshotBeforeUpdate(prevProps, prevState) {

     console.log('#enter getSnapshotBeforeUpdate');

     return 'foo';

 } 

componentDidUpdate(prevProps, prevState, snapshot) {

    console.log('#enter componentDidUpdate snapshot = ', snapshot);

}

getSnapshotBeforeUpdate把snapshot返回,而後DOM改變,而後snapshot傳遞給componentDidUpdate。

總結: 用一個靜態函數getDerivedStateFromProps來取代被清除的幾個生命週期函數,就是強制開發者在render以前只作無反作用的操做,並且能作的操做侷限在根據props和state決定新的state,而已。
這是進一步施加約束,防止開發者亂來

Q&A:
一、Willmount or didmount?

以前想過爲何不在componentWillMount裏寫AJAX獲取數據的功能,以前的觀點是,componentWillMount在render以前執行,早一點執行早獲得結果。

可是,要知道,在componentWillMount裏發起AJAX,無論多快獲得結果也趕不上首次render,

並且componentWillMount在服務器端渲染也會被調用到(固然,也許這是預期的結果),

這樣的IO操做放在componentDidMount裏更合適。在Fiber啓用async render以後,更沒有理由在componentWillMount裏作AJAX,

由於componentWillMount可能會被調用屢次,誰也不會但願無謂地屢次調用AJAX吧。

其實這個問題react的開發者早就有想過,因此才運營而生了後面的新的生命週期,既然不想放在這些很差的生命週期裏面,那都不如直接讓他們乾脆沒辦法作。

二、何時用forceUpdate?

forceUpdate就是從新render。有些變量不在state上,可是你又想達到這個變量更新的時候,刷新render;

或者state裏的某個變量層次太深,更新的時候沒有自動觸發render。

這些時候均可以手動調用forceUpdate自動觸發render。因此建議使用immutable來操做state,redux等flux架構來管理state。

三、父組件的狀態變化怎麼影響子組件的生命週期?

shouldComponentUpdate函數接受nextProps和nextState做爲參數。

你能夠根據props裏面設的值或者state裏面設的值來判斷需不須要更新。例如判斷this.state.count是否等於nextState.count。

四、爲何在react componentWillUpdate中調用setstate會形成循環調用?

若是在shouldComponentUpdate和componentWillUpdate中調用了setState,

此時this._pendingStateQueue != null,則performUpdateIfNecessary方法就會調用updateComponent方法進行組件更新。

可是updateComponent方法又會調用shouldComponentUpdate和componentWillUpdate,

所以形成循環調用,使得瀏覽器內存佔滿後崩潰。

五、函數式組件有沒有生命週期?爲何?

由於函數式組件沒有繼承React.Component,因爲生命週期函數是React.Component類的方法實現的

因此沒繼承這個類,天然就無法使用生命週期函數了

六、PureComponent和Component有什麼區別?

PureComponent優點:

      不須要開發者本身實現shouldComponentUpdate,就能夠進行簡單的判斷來提高性能。

   PureComponent缺點:

      React.PureComponent 經過props和state的淺對比來實現 shouldComponentUpate()。

在PureComponent中,若是包含比較複雜的數據結構,可能會因深層的數據不一致而產生錯誤的否認判斷,致使界面得不到更新。

PureComponent 在使用 shouldComponentUpdate的處理是由 PureComponent 自身來處理,而不是由用戶來控制,

因此咱們在 PureComponent 中若是複寫今生命週期回調函數,React 會提示咱們錯誤的。告訴咱們不容許重寫該方法。

七、爲何PureComponent比較複雜的數據結構,可能會因深層的數據不一致而產生錯誤的否認判斷?

JavaScript 中的對象通常是可變的(Mutable),由於使用了引用賦值,新的對象簡單的引用了原始對象,

改變新的對象將影響到原始對象。

foo={a: 1};

  bar=foo;

  bar.a=2

你會發現此時 foo.a 也被改爲了 2。

爲了解決這個問題,通常的作法是使用 shallowCopy(淺拷貝)或 deepCopy(深拷貝)來避免被修改,但這樣作形成了 CPU 和內存的浪費。

緣由在於js使用的是引用賦值,新的對象簡單引用了原始對象,改變新對象雖然影響了原始對象,

但對象的地址仍是同樣,使用===比較的方式相等。

而在PureComponent中,會被斷定prop相等而不觸發render()。

避免此類問題最簡單的方式是,避免使用值可能會突變的屬性或狀態,而是使用副原本返回新的變量

八、react中的setstate是同步的仍是異步的?Rax呢?

由React控制的事件處理程序,以及生命週期函數調用setState不會同步更新state 。

React控制以外的事件中調用setState是同步更新的。好比原生js綁定的事件,setTimeout/setInterval等。

大部分開發中用到的都是React封裝的事件,好比onChange、onClick、onTouchMove等,這些事件處理程序中的setState都是異步處理的。

Rax的setState是同步的

九、React是怎樣控制異步和同步的?

在 React 的 setState 函數實現中,會根據一個變量 isBatchingUpdates

判斷是直接更新 this.state 仍是放到隊列中延時更新,

而 isBatchingUpdates 默認是 false,表示 setState 會同步更新 this.state;

可是,有一個函數 batchedUpdates,該函數會把 isBatchingUpdates 修改成 true,

而當 React 在調用事件處理函數以前就會先調用這個 batchedUpdates將isBatchingUpdates修改成true,

這樣由 React 控制的事件處理過程 setState 不會同步更新 this.state。

十、setState的一些用法?

(1)多個setState調用會合並處理:

在某些處理程序中調用了兩次setState,可是render只執行了一次。

由於React會將多個this.setState產生的修改放在一個隊列裏進行批延時處理。

(2)參數爲函數的setState用法

假如setState更新state後我但願作一些事情,

而setState多是異步的,那我怎麼知道它何時執行完成。

因此setState提供了函數式用法

接收兩個函數參數,第一個函數調用更新state,第二個函數是更新完以後的回調。

case:

handleClick() {
this.setState({

count: this.state.count + 1

})
}
以上操做存在潛在的陷阱,不該該依靠它們的值來計算下一個狀態。

handleClick() {
this.setState({

count: this.state.count + 1

})
this.setState({

count: this.state.count + 1

})
this.setState({

count: this.state.count + 1

})
}
最終的結果只加了1
由於調用this.setState時,並無當即更改this.state,
因此this.setState只是在反覆設置同一個值而已,上面的代碼等同於這樣

handleClick() {
const count = this.state.count

this.setState({

count: count + 1

})
this.setState({

count: count + 1

})
this.setState({

count: count + 1

})
}
count至關於一個快照,因此無論重複多少次,結果都是加1。

第一個函數接收先前的狀態做爲第一個參數,將這次更新被應用時的props作爲第二個參數。

increment(state, props) {
return {

count: state.count + 1

}
}

handleClick() {
this.setState(this.increment)
this.setState(this.increment)
this.setState(this.increment)
}
對於屢次調用函數式setState的狀況,React會保證調用每次increment時,state都已經合併了以前的狀態修改結果。

也就是說,第一次調用this.setState(increment),

傳給increment的state參數的count是10,第二調用是11,第三次調用是12,最終handleClick執行完成後的結果就是this.state.count變成了13。

值得注意的是:在increment函數被調用時,this.state並無被改變,

依然要等到render函數被從新執行時(或者shouldComponentUpdate函數返回false以後)才被改變,

由於render只執行一次。

相關文章
相關標籤/搜索