React 深入系列,深入讲解了React中的重点概念、特性和模式等,旨在帮助大家加深对React的理解,以及在项目中更加灵活地使用React。
组件是构建React应用的基本单位,组件需要具备数据获取、业务逻辑处理、以及UI呈现的能力,而这些能力是要依赖于组件不同的生命周期方法的。组件的生命周期分为3个阶段:挂载阶段、更新阶段、卸载阶段,每个阶段都包含相应的生命周期方法。因为是深入系列文章,本文不会仔细介绍每个生命周期方法的使用,而是会重点讲解在使用组件生命周期时,经常遇到的疑问和错误使用方式。
服务器数据请求初学者在使用React时,常常不知道何时向服务器发送请求,获取组件所需数据。对于组件所需的初始数据,最合适的地方,是在componentDidMount方法中,进行数据请求,这个时候,组件完成挂载,其代表的DOM已经挂载到页面的DOM树上,即使获取到的数据需要直接操作DOM节点,这个时候也是绝对安全的。有些人还习惯在constructor或者componentWillMount中,进行数据请求,认为这样可以更快的获取到数据,但它们相比componentDidMount的执行时间,提前的时间实在是太微乎其微了。另外,当进行服务器渲染时(SSR),componentWillMount是会被调用两次的,一次在服务器端,一次在客户端,这时候就会导致额外的请求发生。
组件进行数据请求的另一种场景:由父组件的更新导致组件的props发生变化,如果组件的数据请求依赖props,组件就需要重新进行数据请求。例如,新闻详情组件NewsDetail,在获取新闻详情数据时,需要传递新闻的id作为参数给服务器端,当NewsDetail已经处于挂载状态时,如果点击其他新闻,NewsDetail的componentDidMount并不会重新调用,因而componentDidMount中进行新闻详情数据请求的方法也不会再次执行。这时候,应该在componentWillReceiveProps中,进行数据请求:
componentWillReceiveProps(nextProps) { if(this.props.newId !== nextProps.newsId) { fetchNewsDetailById(nextProps.newsId) // 根据最新的新闻id,请求新闻详情数据 } }如果进行数据请求的时机是由页面上的交互行为触发的,例如,点击查询按钮后,查询数据,这时只需要在查询按钮的事件监听函数中,执行数据请求即可,这种情况一般是不会有疑问的。
更新阶段方法的调用组件的更新是组件生命周期中最复杂的阶段,也是涉及到最多生命周期方法的阶段。
正常情况下,当组件发生更新时,组件的生命周期方法的调用顺序如下:
componentWillReceiveProps -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate // 组件收到新的props(props中的数据并不一定真正发生变化)-> 决定是否需要继续执行更新过程 -> 组件代表的虚拟DOM即将更新 -> 组件重新计算出新的虚拟DOM -> 虚拟DOM对应的真实DOM更新到真实DOM树中父组件发生更新或组件自身调用setState,都会导致组件进行更新操作。父组件发生更新导致的组件更新,生命周期方法的调用情况同上所述。如果是组件自身调用setState,导致的组件更新,其生命周期方法的调用情况如下:
shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate可见,这种情况下componentWillReceiveProps并不会被调用。
当组件的shouldComponentUpdate返回false时,组件会停止更新过程,这时候生命周期方法的调用顺序如下:
componentWillReceiveProps -> shouldComponentUpdate -> 结束或(组件自身调用setState,导致的组件更新):
shouldComponentUpdate -> 结束 setState的时机组件的生命周期方法众多,哪些方法中可以调用setState更新组件状态?哪些方法中不可以呢?
可以的方法
componentWillMount、componentDidMount、componentWillReceiveProps、componentDidUpdate
这里有几个注意点:
componentWillMount 中同步调用setState不会导致组件进行额外的渲染,组件经历的生命周期方法依次是componentWillMount -> render -> componentDidMount,组件并不会因为componentWillMount中的setState调用再次进行更新操作。如果是异步调用setState,组件是会进行额外的更新操作。不过实际场景中很少在componentWillMount中调用setState,一般可以通过直接在constructor中定义state的方式代替。
一般情况下,当调用setState后,组件会执行一次更新过程,componentWillReceiveProps等更新阶段的方法会再次被调用,但如果在componentWillReceiveProps中调用setState,并不会额外导致一次新的更新过程,也就是说,当前的更新过程结束后,componentWillReceiveProps等更新阶段的方法不会再被调用一次。(注意,这里仍然指同步调用setState,如果是异步调用,则会导致组件再次进行渲染)