react中componentWillReceiveProps()在props不改變的時候也可能被呼叫
最近在寫react時,需要在props被改變時更新一些東西,所以使用了componentWillReceiveProps
方法,但是卻發現該方法總是在各種沒有改變props的情況下被呼叫,覺得很奇怪,遂詢問我導師(我導師超厲害的!),我導師說這個方法確實有可能在props不改變的情況下被呼叫,所以需要在方法裡手動判斷一下this.props和nextProps是否相同,不相同了才執行我的更新方法。於是我抽空閱讀了一下官方的文件https://developmentarc.gitbooks.io/react-indepth/content/life_cycle/update/component_will_receive_props.html
componentDidMount()
在生命週期中只會被呼叫一次:
- 在最開始的render後被呼叫(在所有的子元素都已經被render之後,並且所有的子元素都已經被呼叫它們的
componentDidMount
s 之後) - 元件在被解除安裝後的render(這確實是一個新的生命週期)
componentWillReceiveProps()
在生命週期的第一次render後不會被呼叫,但是會在之後的每次render中被呼叫 = 當父元件再次傳送props。
在react的update life cycle中,不得不提到componentWillReceiveProps
方法。 該方法在props被傳遞給元件例項時被呼叫。下面詳細地介紹了一下該方法。
該方法當props
發生變化時執行,初始化render
時不執行,在這個回撥函式裡面,你可以根據屬性的變化,通過呼叫this.setState()
來更新你的元件狀態,舊的屬性還是可以通過this.props
來獲取,這裡呼叫更新狀態是安全的,並不會觸發額外的render
呼叫。
componentWillReceiveProps: function(nextProps) { this.setState({ likesIncreasing: nextProps.likeCount > this.props.likeCount }); }
傳遞 props
該方法最典型的用法是:當新的props
被傳遞給元件時。例如,我們有一個Form元件和一個Person元件。 Form元件有一個<input />
,允許使用者通過在其中輸入來更改名稱。 輸入繫結到onChange
事件並設定狀態。狀態值作為props
傳遞給Person元件。
Form.js
import React from 'react';
import Person from './Person';
export default class Form extends React.Component {
constructor(props) {
super(props);
this.state = { name: '' } ;
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({ name: event.currentTarget.value });
}
render() {
return (
<div>
<input type="text" onChange={ this.handleChange } />
<Person name={ this.state.name } />
</div>
);
}
}
當用戶在輸入框輸入時,會使得在Person元件上呼叫componentWillReceiveProps(nextProps)
方法傳入新的prop值。我們可以通過呼叫this.props
來獲得舊的props,並且新值是傳遞給該方法的nextProps
引數。
更新 State
那麼為什麼我們需要componentWillReceiveProps
? 因為在這裡我們能抽取出新的props並且更新內部狀態,比如我們可能有一些狀態,這些狀態是props的計算結果,那麼我們就可以在這個方法裡寫邏輯,使用this.setState()
來儲存結果。
Props may not change
注意:componentWillReceiveProps()
方法的呼叫,並不意味著props的值被改變。
為什麼呢?資料可能在初始渲染和後續兩次更新之間發生變化,而React無法知道資料有沒有變化。 因此,React需要呼叫componentWillReceiveProps,因為元件需要被知會新的props(即使新props恰好與舊props相同)。
假設我們有個prop,稱為 data
,該prop是一個數組,如下所示:
// psuedo code
this.setState({ data: [1, 2, 3] });
<MyComponent data={ this.state.data } />
如果在程式中對該data陣列進行了 push()
操作,使得data陣列的內容發生了變化,但是此時陣列本身(引用)並沒有改變,那麼react便無法判斷是否該陣列內部內容發生了改變,因此為了避免很多問題,也為了避免要做花費很多的深層比較,react會呼叫componentWillReceiveProps()方法。
因此 componentWillReceiveProps()
方法允許我們去檢查、判斷是否進來的新props發生了變更,讓我們自己去做決定。我們只需要知道:當該方法被呼叫時,並不意味著props肯定會不同。(講得十分透徹,值得一讀。)
你也許認為React應該使用更聰明的檢查機制來判斷props是否相等,但是這樣會存在幾個問題:
- 當舊的props和新的props實際上是相同的物理物件時(只更改了物件的內部值),由於引用是triple-equals-equal,所以進行相等性的檢查並不能告訴我們該值是否已更改,唯一可能的解決方案是建立資料的深層拷貝,然後再進行深入比較。但對於大型資料結構(特別是具有周期的資料結構)而言,所花費的代價可能過於昂貴。
- props物件可能會包含 對函式的引用,這些函式可能會捕獲閉包中的變數。 而React無法窺視這些閉包,因此React無法拷貝它們or驗證相等性。
- props物件可能包含對父物件渲染過程中重新例項化的物件的引用(即不是triple-equals-equal),但在概念上相等(即相同的鍵和相同的值)。深度比較(代價昂貴)可以檢測到這一點,除了函式,因為沒有可靠的方法來比較兩個函式以判斷它們是否在語義上等價。
Skipping this method
與 Mounting 階段的其他方法不同,並不是所有的更新階段的方法都會每次被呼叫。 例如,如果update僅僅是因為一次狀態更改而被觸發,那麼componentWillReceiveProps()將會被跳過。 回到我們上面的Form.js例子:
// ...
handleChange(event) {
this.setState({ name: event.currentTarget.value });
}
render() {
return (
<div>
<input type="text" onChange={ this.handleChange } />
<Person name={ this.state.name } />
</div>
);
}
// ...
當用戶在輸入框中輸入時,將會觸發一個setState()
方法。這將在Form元件和Person元件中觸發Update phase。對於Form元件來說,沒有收到新的props,所以componentWillReceiveProps()將會被跳過。