在咱們編寫React代碼的時候,總會遇到這麼一個問題:請求接口並展現咱們獲取到的數據。這聽起來很簡單,可是你有沒有想過一個問題:在什麼時候進行進行網絡請求才是最好的?我相信大家都會說,我固然知道,在 componentDidMount
中啊,由於這是React官方推薦的,不服上個圖javascript
這一下你應該服了吧?服是服了,但爲何是 componentDidMount
? constructor
或 componentWillMount
不能夠嗎?java
首先咱們來百度一下,這是一個最高讚的答案react
總結一下:segmentfault
componentDidmount
是在組件徹底掛載後纔會執行,在此方法中調用setState
會觸發從新渲染,最重要的是,這是官方推薦的!跨域
constructor
調用是在一開始,組件未掛載,因此不能用。網絡
componentWillMount
調用在 constructor
後,在這裏的代碼調用 setState
不會出發從新渲染,因此不用。異步
還有一個沒有出如今這裏但聽得最多的說法是:在 componentWillMount
裏進行網絡請求會阻礙組件的渲染。函數
反正就是要在 componentDidmount
裏用!測試
說的好像挺有道理的,可是也感受怪怪的,看的再多不如本身動手測試一下。首先測試一下 constructor
。fetch
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
text: 'plain text'
};
fetch('https://s.codepen.io')
.then(res => this.setState({text: 'success'}))
.catch(err => this.setState({text: 'error'}))
}
render() {
return (
<div> <h1>{this.state.text}</h1> </div>
);
}
}
ReactDOM.render(
<Parent/>,
document.getElementById('root')
);
複製代碼
戳這裏看演示,state從一開始的plain text變成了error(由於跨域問題,請求沒法成功,可是沒有關係)
因此上面說的constructor
調用時組件未掛載,因此不能用的說法是錯誤的,組件未掛載也能夠發送請求,這裏所影響的時間只有執行發送請求的時間,而後組件接着渲染,等異步數據返回後,再執行 setState
,或許你會說,若是請求時間很短,在組件掛載以前就返回了怎麼辦,此時的 setState
還會起做用嗎?彆着急,這個問題後面會提到。
將請求移到 componentWillMount
中
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
text: 'plain text'
};
}
componentWillMount() {
fetch('https://s.codepen.io')
.then(res => this.setState({text: 'success'}))
.catch(err => this.setState({text: 'error'}))
}
render() {
return (
<div> <h1>{this.state.text}</h1> </div>
);
}
}
ReactDOM.render(
<Parent/>,
document.getElementById('root')
);
複製代碼
戳這裏,能夠看到,state也是從plain text 變成了error,嫌太快看不清楚的能夠用setTimeout模擬一下。這就很奇怪了,不是說willMount裏面setState不會從新渲染嗎?不是說網絡請求會阻塞組件的渲染嗎?然而都沒有,其實原理跟constructor是同樣的,所影響的時間只有執行發送請求的時間,並不會阻塞組件的渲染,但不推薦使用 componentWillMount
是有其餘的緣由:
很重要的一點,React16.3後將會廢棄掉componentWillMount、componentWillReceiveProps 以及 componentWillUpdate 三個周期函數,直到React 17前還可使用,不過會有一個警告。
跟服務端渲染有關係(同構),若是在 componentWillMount
裏獲取數據,fetch data會執行兩次,一次在服務端一次在客戶端,使用 componentDidMount
則沒有這個問題。
至於前面說到的數據在組件掛載前返回致使不生效的,這種狀況並不會發生, 由於 setState
是將更新的狀態放進了組件的__pendingStateQueue隊列中,react並不會當即響應更新,會等到組件掛載完成後,再統一更新髒組件,見下圖
所以,從另外的角度看,放在constructor或者componentWillMount裏面反而會更加有效率。
感謝@名揚的提醒,React16引入了React Fiber
的概念,致使了componentWillMount
,componentWillReceiveProps
,shouldComponentUpdate
,componentWillUpdate
這些生命週期出現了可能被調用不止一次的可能,詳情參見React Fiber是什麼?,這應該也是上文提到的這些生命週期將會被廢棄的緣由。
數據獲取能夠放在 constructor
或者 componentDidmount
中,不建議放在 componentWillMount
。 可是爲了更好的代碼規範和可讀性,建議統一放在 componentDidmount
。
對於首次render沒有數據,可能致使出錯的。能夠設置一個initial state,或者增長一個loading狀態,加載數據時展現一個spinner或者骨架圖都是比較經常使用的方案。