爲什麼要在componentDidMount裏面發送請求?

在咱們編寫React代碼的時候,總會遇到這麼一個問題:請求接口並展現咱們獲取到的數據。這聽起來很簡單,可是你有沒有想過一個問題:在什麼時候進行進行網絡請求才是最好的?我相信大家都會說,我固然知道,在 componentDidMount 中啊,由於這是React官方推薦的,不服上個圖javascript

pic1

這一下你應該服了吧?服是服了,但爲何是 componentDidMountconstructorcomponentWillMount不能夠嗎?java

首先咱們來百度一下,這是一個最高讚的答案react

pic2

總結一下:segmentfault

  1. componentDidmount 是在組件徹底掛載後纔會執行,在此方法中調用setState 會觸發從新渲染,最重要的是,這是官方推薦的!跨域

  2. constructor 調用是在一開始,組件未掛載,因此不能用。網絡

  3. componentWillMount 調用在 constructor 後,在這裏的代碼調用 setState 不會出發從新渲染,因此不用。異步

  4. 還有一個沒有出如今這裏但聽得最多的說法是:在 componentWillMount 裏進行網絡請求會阻礙組件的渲染。函數

  5. 反正就是要在 componentDidmount 裏用!測試

說的好像挺有道理的,可是也感受怪怪的,看的再多不如本身動手測試一下。首先測試一下 constructorfetch

constructor

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(由於跨域問題,請求沒法成功,可是沒有關係)

pic3

因此上面說的constructor 調用時組件未掛載,因此不能用的說法是錯誤的,組件未掛載也能夠發送請求,這裏所影響的時間只有執行發送請求的時間,而後組件接着渲染,等異步數據返回後,再執行 setState,或許你會說,若是請求時間很短,在組件掛載以前就返回了怎麼辦,此時的 setState 還會起做用嗎?彆着急,這個問題後面會提到。

componentWillMount

將請求移到 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 是有其餘的緣由:

  1. 很重要的一點,React16.3後將會廢棄掉componentWillMount、componentWillReceiveProps 以及 componentWillUpdate 三個周期函數,直到React 17前還可使用,不過會有一個警告。

  2. 跟服務端渲染有關係(同構),若是在 componentWillMount 裏獲取數據,fetch data會執行兩次,一次在服務端一次在客戶端,使用 componentDidMount 則沒有這個問題。

至於前面說到的數據在組件掛載前返回致使不生效的,這種狀況並不會發生, 由於 setState 是將更新的狀態放進了組件的__pendingStateQueue隊列中,react並不會當即響應更新,會等到組件掛載完成後,再統一更新髒組件,見下圖

pic4

所以,從另外的角度看,放在constructor或者componentWillMount裏面反而會更加有效率。

React Fiber

感謝@名揚的提醒,React16引入了React Fiber的概念,致使了componentWillMountcomponentWillReceivePropsshouldComponentUpdatecomponentWillUpdate這些生命週期出現了可能被調用不止一次的可能,詳情參見React Fiber是什麼?,這應該也是上文提到的這些生命週期將會被廢棄的緣由。

總結

  1. 數據獲取能夠放在 constructor 或者 componentDidmount 中,不建議放在 componentWillMount。 可是爲了更好的代碼規範和可讀性,建議統一放在 componentDidmount

  2. 對於首次render沒有數據,可能致使出錯的。能夠設置一個initial state,或者增長一個loading狀態,加載數據時展現一個spinner或者骨架圖都是比較經常使用的方案。

參考連接

  1. Where to Fetch Data: componentWillMount vs componentDidMount

  2. React數據獲取爲何必定要在componentDidMount裏面調用?

  3. React Fiber是什麼?

相關文章
相關標籤/搜索