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

StaBleakley 7年前
   <p style="text-align:center"><img src="https://simg.open-open.com/show/a3ed445ab7e87a459211683f9b22d395.png"></p>    <p>偶尔发现,React的生命周期函数可以写成async的形式,比如,componentDidMount可以写成这样。</p>    <pre>  <code class="language-java">async componentDidMount() {       //函数体中可以使用await    }</code></pre>    <p>async/await可以简化异步操作的代码,用同步的形式表示异步的过程,这个语法,没有赶上ES6标准,也没有赶上ES7标准,但是,因为Babel的存在,实际上使用起来没有任何障碍。</p>    <p>因为Babel的支持,把React的生命周期函数时限为async函数其实也没有什么神奇之处,因为React要做的只是去调用各个生命周期函数,而已,而async函数被调用之后,返回的是一个Promise对象。</p>    <p>要注意,有些生命周期函数返回Promise对象无所谓,比如componentDidMount,因为它的返回值本来React就不关心。</p>    <p>还有一些生命周期函数返回Promise对象就扯淡了,比如render,把render写成async是肯定不行的,因为React预期render返回一个组件的结构或者null或者undefined,返回一个Promise算什么事,React也不会去等待Promise成功完结,也不会去拿让Promise成功完结的值。</p>    <p>还有一些生命周期函数返回Promise对象会产生你意向不到的结果,比如shouldComponentUpdate,比如这样写。</p>    <pre>  <code class="language-java">async shouldComponentUpdate() {      return false; //实际上不会这么写,应该根据state和prop计算出false或者true    }</code></pre>    <p>表面上看,shouldComponentUpdate返回的是false,组件不会被重新渲染,其实,因为函数声明为async,所以每次返回的是一个Promise.resolve(false),一个Promise对象被转化为bool类型,绝对是true,所以实际上在React看来shouldComponentUpdate返回的永远是true。</p>    <p>而且,在React v16中引入了Fiber架构,一旦将来React版本中打开异步渲染(async rendering)的功能,那么,render之前的生命周期函数在一次渲染中都可能被调用多次(具体原因参见《 <a href="/misc/goto?guid=4959755082339236923" rel="nofollow,noindex">深入理解React v16新功能</a> 》),所以,我们应该尽量保持render之前的生命周期函数不要产生副作用。</p>    <p>异步操作天生就是有副作用的操作,所以, <strong>不要把render之前的生命周期函数标记为async</strong> 。</p>    <p>适合使用async的生命周期函数要满足这两个条件:</p>    <ol>     <li>要在render函数之后执行(也就是Fiber中的Phase 2阶段函数)</li>     <li>React不关心这个生命周期函数的返回值</li>    </ol>    <p>如此说来,似乎也只有render之后的两个生命周期函数componentDidUpdate和componentDidMount可以用上async这招,实际上,在这两个函数中做AJAX的异步操作也是一种普遍接受的模式。</p>    <p>比如,我们可以这么写。</p>    <pre>  <code class="language-java">async componentDidMount() {      const res = await fetch('https://api.github.com/repos/非死book/react')      const json = await res.json()      this.setState({reactStargazersCount: json.stargazers_count});    }</code></pre>    <p>上面其实是一个异步的过程,但是看起来却一个回调函数都没用,就和同步代码一样,这就是async/await的好处。</p>    <p>理论上,componentWillMount这样的函数中也可以使用async,但是千万不要以为React会因此而异步处理componentWillMount,React只是调用componentWillMount一次而已,然后componentWillMount中的await会依次执行,但是React才不会等它呢,直接就同步往下运行去执行其他生命周期函数了。</p>    <p>看这个例子,在componentWillMount中获取Redux在github上的星数,在componentDidMount中获取React在github上的星数。</p>    <pre>  <code class="language-java">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});    }  };</code></pre>    <p>在console中可以看到输出:</p>    <pre>  <code class="language-java">#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</code></pre>    <p>从这段log可以清晰地看到,componentWillMount使用async根本不会阻断React去调用render,shouldComponentUpdate用async返回一个fals而依然会被认为返回布尔的true。</p>    <p>async/await这招,还是主要在componentDidMount和componentDidUpdate里用合适。</p>    <p>来自: <a href="https://zhuanlan.zhihu.com/p/30401565?utm_source=tuicool&utm_medium=referral">https://zhuanlan.zhihu.com/p/30401565</a></p>