React中的async/await生命周期函數

StaBleakley 7年前發布 | 23K 次閱讀 React ReactNative 移動開發

偶爾發現,React的生命周期函數可以寫成async的形式,比如,componentDidMount可以寫成這樣。

async componentDidMount() {
     //函數體中可以使用await
  }

async/await可以簡化異步操作的代碼,用同步的形式表示異步的過程,這個語法,沒有趕上ES6標準,也沒有趕上ES7標準,但是,因為Babel的存在,實際上使用起來沒有任何障礙。

因為Babel的支持,把React的生命周期函數時限為async函數其實也沒有什么神奇之處,因為React要做的只是去調用各個生命周期函數,而已,而async函數被調用之后,返回的是一個Promise對象。

要注意,有些生命周期函數返回Promise對象無所謂,比如componentDidMount,因為它的返回值本來React就不關心。

還有一些生命周期函數返回Promise對象就扯淡了,比如render,把render寫成async是肯定不行的,因為React預期render返回一個組件的結構或者null或者undefined,返回一個Promise算什么事,React也不會去等待Promise成功完結,也不會去拿讓Promise成功完結的值。

還有一些生命周期函數返回Promise對象會產生你意向不到的結果,比如shouldComponentUpdate,比如這樣寫。

async shouldComponentUpdate() {
    return false; //實際上不會這么寫,應該根據state和prop計算出false或者true
  }

表面上看,shouldComponentUpdate返回的是false,組件不會被重新渲染,其實,因為函數聲明為async,所以每次返回的是一個Promise.resolve(false),一個Promise對象被轉化為bool類型,絕對是true,所以實際上在React看來shouldComponentUpdate返回的永遠是true。

而且,在React v16中引入了Fiber架構,一旦將來React版本中打開異步渲染(async rendering)的功能,那么,render之前的生命周期函數在一次渲染中都可能被調用多次(具體原因參見《 深入理解React v16新功能 》),所以,我們應該盡量保持render之前的生命周期函數不要產生副作用。

異步操作天生就是有副作用的操作,所以, 不要把render之前的生命周期函數標記為async 。

適合使用async的生命周期函數要滿足這兩個條件:

  1. 要在render函數之后執行(也就是Fiber中的Phase 2階段函數)
  2. React不關心這個生命周期函數的返回值

如此說來,似乎也只有render之后的兩個生命周期函數componentDidUpdate和componentDidMount可以用上async這招,實際上,在這兩個函數中做AJAX的異步操作也是一種普遍接受的模式。

比如,我們可以這么寫。

async componentDidMount() {
    const res = await fetch('https://api.github.com/repos/非死book/react')
    const json = await res.json()
    this.setState({reactStargazersCount: json.stargazers_count});
  }

上面其實是一個異步的過程,但是看起來卻一個回調函數都沒用,就和同步代碼一樣,這就是async/await的好處。

理論上,componentWillMount這樣的函數中也可以使用async,但是千萬不要以為React會因此而異步處理componentWillMount,React只是調用componentWillMount一次而已,然后componentWillMount中的await會依次執行,但是React才不會等它呢,直接就同步往下運行去執行其他生命周期函數了。

看這個例子,在componentWillMount中獲取Redux在github上的星數,在componentDidMount中獲取React在github上的星數。

class Demo extends React.Component {
  constructor() {
    super(...arguments);

    this.state = {
      reactStargazersCount: '',
      reduxStargazersCount: '',
    };
  }

  async componentWillMount() {
    console.log('#enter componentWillMount');
    const res = await fetch('https://api.github.com/repos/reactjs/redux')
    console.log('#after get response in componentWillMount');
    const json = await res.json()
    console.log('#after get json in componentWillMount');
    this.setState({reduxStargazersCount: json.stargazers_count});
  }

  async shouldComponentUpdate() {
    console.log('#enter shouldComponentUpdate');
    return false;
  }

  render() {
    console.log('#enter render');
    return (
      <div>
        <div>React stargazers count: {this.state.reactStargazersCount} </div>
        <div>Redux stargazers count: {this.state.reduxStargazersCount} </div>
      </div>
    );
  }

  async componentDidMount() {
    console.log('#enter componentDidMount');
    const res = await fetch('https://api.github.com/repos/非死book/react')
    console.log('#after get response in componentDidMount');
    const json = await res.json()
    console.log('#after get json in componentDidMount');
    this.setState({reactStargazersCount: json.stargazers_count});
  }
};

在console中可以看到輸出:

#enter componentWillMount
#enter render
#enter componentDidMount
#after get response in componentWillMount
#after get json in componentWillMount
#enter shouldComponentUpdate
#enter render
#after get response in componentDidMount
#after get json in componentDidMount
#enter shouldComponentUpdate
#enter render

從這段log可以清晰地看到,componentWillMount使用async根本不會阻斷React去調用render,shouldComponentUpdate用async返回一個fals而依然會被認為返回布爾的true。

async/await這招,還是主要在componentDidMount和componentDidUpdate里用合適。

來自: https://zhuanlan.zhihu.com/p/30401565

 本文由用戶 StaBleakley 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!