为什么要在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是什么?

相关文章
相关标签/搜索