關於react生命週期的文章,網上一大堆,本人也看了許多,可是以爲大部分人寫的都是照搬其它人的沒有本身獨到的看法,因此決定根據本人的實戰經驗和我的理解再寫一篇React生命週期的文章,因爲React目前已更新到16.4版本,因此重點講解React v16.4變化的生命週期,以前的生命週期函數會一帶而過react
先整體看下React16的生命週期圖canvas
React16廢棄的三個生命週期函數瀏覽器
注:目前在16版本中
componentWillMount,componentWillReceiveProps,componentWillUpdate並未徹底刪除這三個生命週期函數,並且新增了UNSAFE_componentWillMount,UNSAFE_componentWillReceiveProps,UNSAFE_componentWillUpdate三個函數,官方計劃在17版本徹底刪除這三個函數,只保留UNSAVE_前綴的三個函數,目的是爲了向下兼容,可是對於開發者而言應該儘可能避免使用他們,而是使用新增的生命週期函數替代它們bash
取而代之的是兩個新的生命週期函數服務器
咱們將React的生命週期分爲三個階段,而後詳細講解每一個階段具體調用了什麼函數,這三個階段是:微信
掛載階段,也能夠理解爲組件的初始化階段,就是將咱們的組件插入到DOM中,只會發生一次網絡
這個階段的生命週期函數調用以下:app
組件構造函數,第一個被執行異步
若是沒有顯示定義它,咱們會擁有一個默認的構造函數svg
若是顯示定義了構造函數,咱們必須在構造函數第一行執行super(props),不然咱們沒法在構造函數裏拿到this對象,這些都屬於ES6的知識
在構造函數裏面咱們通常會作兩件事:
constructor(props) {
super(props)
this.state = {
select,
height: 'atuo',
externalClass,
externalClassText
}
this.handleChange1 = this.handleChange1.bind(this)
this.handleChange2 = this.handleChange2.bind(this)
}
複製代碼
禁止在構造函數中調用setState,能夠直接給state設置初始值
static getDerivedStateFromProps(nextProps, prevState)
一個靜態方法,因此不能在這個函數裏面使用this,這個函數有兩個參數props和state,分別指接收到的新參數和當前的state對象,這個函數會返回一個對象用來更新當前的state對象,若是不須要更新能夠返回null
該函數會在掛載時,接收到新的props,調用了setState和forceUpdate時被調用
在React v16.3時只有在掛載時和接收到新的props被調用,聽說這是官方的失誤,後來修復了
這個方法就是爲了取代以前的componentWillMount、componentWillReceiveProps和componentWillUpdate
當咱們接收到新的屬性想去修改咱們state,可使用getDerivedStateFromProps
class ExampleComponent extends React.Component {
state = {
isScrollingDown: false,
lastRow: null
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.currentRow !== prevState.lastRow) {
return {
isScrollingDown:
nextProps.currentRow > prevState.lastRow,
lastRow: nextProps.currentRow
}
}
return null
}
}
複製代碼
在16版本這兩個方法並存,可是在17版本中componentWillMount被刪除,只保留UNSAFE_componentWillMount,目的是爲了作向下兼容,對於新的應用,用getDerivedStateFromProps代替它們
因爲componentWillMount/ UNSAFE_componentWillMount是在render以前調用,因此就算在這個方法中調用setState也不會觸發從新渲染(re-render)
React中最核心的方法,一個組件中必需要有這個方法
返回的類型有如下幾種:
關於Fragment和Portals是React16新增的,若是你們不清楚能夠去閱讀官方文檔,在這裏就不展開了
render函數是純函數,裏面只作一件事,就是返回須要渲染的東西,不該該包含其它的業務邏輯,如數據請求,對於這些業務邏輯請移到componentDidMount和componentDid Update中
組件裝載以後調用,此時咱們能夠獲取到DOM節點並操做,好比對canvas,svg的操做,服務器請求,訂閱均可以寫在這個裏面,可是記得在componentWillUnmount中取消訂閱
componentDidMount() {
const { progressCanvas, progressSVG } = this
const canvas = progressCanvas.current
const ctx = canvas.getContext('2d')
canvas.width = canvas.getBoundingClientRect().width
canvas.height = canvas.getBoundingClientRect().height
const svg = progressSVG.current
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
rect.setAttribute('x', 0)
rect.setAttribute('y', 0)
rect.setAttribute('width', 0)
rect.setAttribute('height', svg.getBoundingClientRect().height)
rect.setAttribute('style', 'fill:red')
const animate = document.createElementNS('http://www.w3.org/2000/svg', 'animate')
animate.setAttribute('attributeName', 'width')
animate.setAttribute('from', 0)
animate.setAttribute('to', svg.getBoundingClientRect().width)
animate.setAttribute('begin', '0ms')
animate.setAttribute('dur', '1684ms')
animate.setAttribute('repeatCount', 'indefinite')
animate.setAttribute('calcMode', 'linear')
rect.appendChild(animate)
svg.appendChild(rect)
svg.pauseAnimations()
this.canvas = canvas
this.svg = svg
this.ctx = ctx
}
複製代碼
在componentDidMount中調用setState會觸發一次額外的渲染,多調用了一次render函數,可是用戶對此沒有感知,由於它是在瀏覽器刷新屏幕前執行的,可是咱們應該在開發中避免它,由於它會帶來必定的性能問題,咱們應該在constructor中初始化咱們的state對象,而不該該在componentDidMount調用state方法
更新階段,當組件的props改變了,或組件內部調用了setState或者forceUpdate發生,會發生屢次
這個階段的生命週期函數調用以下:
componentWillReceiveProps(nextProps, prevState) UNSAFE_componentWillReceiveProps(nextProps, prevState)
在16版本這兩個方法並存,可是在17版本中componentWillReceiveProps被刪除,UNSAFE_componentWillReceiveProps,目的是爲了作向下兼容,對於新的應用,用getDerivedStateFromProps代替它們
注意,當咱們父組件從新渲染的時候,也會致使咱們的子組件調用componentWillReceiveProps/UNSAFE_componentWillReceiveProps,即便咱們的屬性和以前的同樣,因此須要咱們在這個方法裏面去進行判斷,若是先後屬性不一致纔去調用setState
在裝載階段這兩個函數不會被觸發,在組件內部調用了setState和forceUpdate也不會觸發這兩個函數
這個方法在裝載階段已經講過了,這裏再也不贅述,記住在更新階段,不管咱們接收到新的屬性,調用了setState仍是調用了forceUpdate,這個方法都會被調用
shouldComponentUpdate(nextProps, nextState)
有兩個參數nextProps和nextState,表示新的屬性和變化以後的state,返回一個布爾值,true表示會觸發從新渲染,false表示不會觸發從新渲染,默認返回true
注意當咱們調用forceUpdate並不會觸發此方法
由於默認是返回true,也就是隻要接收到新的屬性和調用了setState都會觸發從新的渲染,這會帶來必定的性能問題,因此咱們須要將this.props與nextProps以及this.state與nextState進行比較來決定是否返回false,來減小從新渲染
可是官方提倡咱們使用PureComponent來減小從新渲染的次數而不是手工編寫shouldComponentUpdate代碼,具體該怎麼選擇,全憑開發者本身選擇
在將來的版本,shouldComponentUpdate返回false,仍然可能致使組件從新的渲染,這是官方本身說的
Currently, if shouldComponentUpdate() returns false, then UNSAFE_componentWillUpdate(), render(), and componentDidUpdate() will not be invoked. In the future React may treat shouldComponentUpdate() as a hint rather than a strict directive, and returning false may still result in a re-rendering of the component.
componentWillUpdate(nextProps, nextState)
UNSAFE_componentWillUpdate(nextProps, nextState)
在16版本這兩個方法並存,可是在17版本中componentWillUpdate被刪除,UNSAFE_componentWillUpdate,目的是爲了作向下兼容
在這個方法裏,你不能調用setState,由於能走到這個方法,說明shouldComponentUpdate返回true,此時下一個state狀態已經被肯定,立刻就要執行render從新渲染了,不然會致使整個生命週期混亂,在這裏也不能請求一些網絡數據,由於在異步渲染中,可能會致使網絡請求屢次,引發一些性能問題,
若是你在這個方法裏保存了滾動位置,也是不許確的,仍是由於異步渲染的問題,若是你非要獲取滾動位置的話,請在getSnapshotBeforeUpdate調用
更新階段也會觸發,裝載階段已經講過了,再也不贅述
getSnapshotBeforeUpdate(prevProps, prevState)
這個方法在render以後,componentDidUpdate以前調用,有兩個參數prevProps和prevState,表示以前的屬性和以前的state,這個函數有一個返回值,會做爲第三個參數傳給componentDidUpdate,若是你不想要返回值,請返回null,不寫的話控制檯會有警告
還有這個方法必定要和componentDidUpdate一塊兒使用,不然控制檯也會有警告
前面說過這個方法時用來代替componentWillUpdate/UNSAVE_componentWillUpdate,下面舉個例子說明下:
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>
);
}
}
複製代碼
componentDidUpdate(prevProps, prevState, snapshot)
該方法在getSnapshotBeforeUpdate方法以後被調用,有三個參數prevProps,prevState,snapshot,表示以前的props,以前的state,和snapshot。第三個參數是getSnapshotBeforeUpdate返回的
在這個函數裏咱們能夠操做DOM,和發起服務器請求,還能夠setState,可是注意必定要用if語句控制,不然會致使無限循環
卸載階段,當咱們的組件被卸載或者銷燬了
這個階段的生命週期函數只有一個:
當咱們的組件被卸載或者銷燬了就會調用,咱們能夠在這個函數裏去清除一些定時器,取消網絡請求,清理無效的DOM元素等垃圾清理工做
注意不要在這個函數裏去調用setState,由於組件不會從新渲染了
大家的打賞是我寫做的動力