转自
在我们编写React代码的时候,总会遇到这么一个问题:请求接口并展示我们获取到的数据。这听起来很简单,但是你有没有想过一个问题:在何时进行进行网络请求才是最好的?我相信你们都会说,我当然知道,在 componentDidMount
中啊,因为这是React官方推荐的,不服上个图
这一下你应该服了吧?服是服了,但为什么是 componentDidMount
? constructor
或 componentWillMount
不可以吗?
首先我们来百度一下,这是一个最高赞的答案
总结一下:
-
componentDidmount
是在组件完全挂载后才会执行,在此方法中调用setState
会触发重新渲染,最重要的是,这是官方推荐的! -
constructor
调用是在一开始,组件未挂载,所以不能用。 -
componentWillMount
调用在constructor
后,在这里的代码调用setState
不会出发重新渲染,所以不用。 -
还有一个没有出现在这里但听得最多的说法是:在
componentWillMount
里进行网络请求会阻碍组件的渲染。 -
反正就是要在
componentDidmount
里用!
说的好像挺有道理的,但是也感觉怪怪的,看的再多不如自己动手测试一下。首先测试一下 constructor
。
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(因为跨域问题,请求无法成功,但是没有关系)
所以上面说的constructor
调用时组件未挂载,所以不能用的说法是错误的,组件未挂载也可以发送请求,这里所影响的时间只有执行发送请求的时间,然后组件接着渲染,等异步数据返回后,再执行 setState
,或许你会说,如果请求时间很短,在组件挂载之前就返回了怎么办,此时的 setState
还会起作用吗?别着急,这个问题后面会提到。
componentWillMount
将请求移到 componentWillMount
中
class Parent extends React.Component { constructor(props) { super(props);
this.state = { text: 'plain text'};复制代码
}
componentWillMount() { fetch('') .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会执行两次,一次在服务端一次在客户端,使用componentWillMount
则没有这个问题。
至于前面说到的数据在组件挂载前返回导致不生效的,这种情况并不会发生, 因为 setState
是将更新的状态放进了组件的__pendingStateQueue队列中,react并不会立即响应更新,会等到组件挂载完成后,再统一更新脏组件,见下图
因此,从另外的角度看,放在constructor或者componentWillMount里面反而会更加有效率。
总结
-
数据获取可以放在
constructor
或者componentDidmount
中,不建议放在componentWillMount
。 但是为了更好的代码规范和可读性,建议统一放在componentDidmount
。 -
对于首次render没有数据,可能导致出错的。可以设置一个initial state,或者增加一个loading状态,加载数据时展示一个spinner或者骨架图都是比较常用的方案。