文:徐超,《React进阶之路》作者

授权发布,转载请注明作者及出处


React 深入系列4:组件的生命周期

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

    这里有几个注意点:

    1. componentWillMount 中同步调用setState不会导致组件进行额外的渲染,组件经历的生命周期方法依次是componentWillMount -> render -> componentDidMount,组件并不会因为componentWillMount中的setState调用再次进行更新操作。如果是异步调用setState,组件是会进行额外的更新操作。不过实际场景中很少在componentWillMount中调用setState,一般可以通过直接在constructor中定义state的方式代替。
    2. 一般情况下,当调用setState后,组件会执行一次更新过程,componentWillReceiveProps等更新阶段的方法会再次被调用,但如果在componentWillReceiveProps中调用setState,并不会额外导致一次新的更新过程,也就是说,当前的更新过程结束后,componentWillReceiveProps等更新阶段的方法不会再被调用一次。(注意,这里仍然指同步调用setState,如果是异步调用,则会导致组件再次进行渲染)
    3. componentDidUpdate中调用setState要格外小心,在setState前必须有条件判断,只有满足了相应条件,才setState,否组组件会不断执行更新过程,进入死循环。因为setState会导致新一次的组件更新,组件更新完成后,componentDidUpdate被调用,又继续setState,死循环就产生了。
  • 不可以的方法

    其他生命周期方法都不能调用setState,主要原因有两个:

    1. 产生死循环。例如,shouldComponentUpdate、componentWillUpdate 和 render 中调用setState,组件本次的更新还没有执行完成,又会进入新一轮的更新,导致不断循环更新,进入死循环。
    2. 无意义。componentWillUnmount 调用时,组件即将被卸载,setState是为了更新组件,在一个即将卸载的组件上更新state显然是无意义的。实际上,在componentWillUnmount中调用setState也是会抛出异常的。

render次数 != 浏览器界面更新次数

先看下面的一个例子:

class App extends React.Component {constructor(props) {super(props)this.state = {bgColor: "red"}}render() {var {bgColor} = this.statereturn (<div style = {{backgroundColor: bgColor}}> Test</div>);}componentDidMount() {this.setState({bgColor: "yellow"})}
}

当我们观察浏览器渲染出的页面时,页面中Test所在div的背景色,是先显示红色,再变成黄色呢?还是直接就显示为黄色呢?

答案是:直接就显示为黄色!

这个过程中,组件的生命周期方法被调用的顺序如下:

constructor -> componentWillMount -> render -> componentDidMount -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate

组件在挂载完成后,因为setState的调用,将立即执行一次更新过程。虽然render方法被调用了两次,但这并不会导致浏览器界面更新两次,实际上,两次DOM的修改会合并成一次浏览器界面的更新。React官网介绍componentDidMount方法时也有以下说明:

Calling setState() in this method will trigger an extra rendering, but it will happen before the browser updates the screen. This guarantees that even though the render() will be called twice in this case, the user won’t see the intermediate state.

这说明,组件render的次数 不一定等于 浏览器界面更新次数。虽然JS的执行和DOM的渲染分别由浏览器不同的线程完成,但JS的执行会阻塞DOM的渲染,而上面的两次render是在一个JS事件周期内执行的,所以在两次render结束前,浏览器不会更新界面。

下篇预告:

React 深入系列5:事件处理


新书推荐《React进阶之路》

作者:徐超

毕业于浙江大学,硕士,资深前端工程师,长期就职于能源物联网公司远景智能。8年软件开发经验,熟悉大前端技术,拥有丰富的Web前端和移动端开发经验,尤其对React技术栈和移动Hybrid开发技术有深入的理解和实践经验。



美团点评广告平台大前端团队招收2019\2020年前端实习生(偏动效方向)

有意者邮件:yao.zhou@meituan.com

转载于:https://www.cnblogs.com/ikcamp/p/8920873.html

React 深入系列4:组件的生命周期相关推荐

  1. react学习笔记(4)组件的生命周期(运行阶段和销毁阶段)以及事件处理函数

    1.组件的生命周期 接着(2)中的组件生命周期 1.运行阶段 运行阶段有5个步骤: componentWillReceiveProps: 父组件修改属性触发,可以修改新属性,修改状态. shouldC ...

  2. 03 为什么 React 16 要更改组件的生命周期?(下)

    通过对上一个课时的学习,你已经对 React 15 的生命周期有了系统的掌握和理解.本课时,我将在此基础上,对 React 16 以来的生命周期进行剖析.在理解"是什么"的基础上, ...

  3. 从零学React Native之12 组件的生命周期

    一个React Native组件从它被加载,到最终被卸载会经历一个完整的生命周期.所谓生命周期,就是一个对象从开始生成到最后消亡所经历的状态,理解生命周期,是合理开发的关键. ES6语法和之前的ES5 ...

  4. Android之React Native 中组件的生命周期

    React Native 中组件的生命周期 概述 就像 Android 开发中的 View 一样,React Native(RN) 中的组件也有生命周期(Lifecycle).所谓生命周期,就是一个对 ...

  5. React简介、虚拟DOM、Diff算法、创建React项目、JSX语法、组件、组件声明方式、组件传值props和state、组件的生命周期

    React简介: 前面只是简单介绍移动APP开发,后面还会继续深入介绍移动app开发:其中想要用ReactNative开发出更出色的应用,那么就得学好React,下面将介绍React: React 是 ...

  6. native react 更新机制_React Native - 组件的生命周期详解(附:各阶段调用的方法)...

    一个 React Native 组件从它被 React Native框架加载,到最终被 React Native 框架卸载,会经历一个完整的生命周期. 在这个生命周期中,我们可以定义一些生命周期函数, ...

  7. React 初探 [五] React 组件的生命周期

    说起生命周期,最先接触的是Andorid 开发中 Activity 和 Fragment 的生命周期,再者是Vue 组件,那么今天要梳理的是React 组件的生命周期,可见对生命周期的理解和掌握对组件 ...

  8. React 组件的生命周期

    目录 前言 一.React 实例的生命周期及其与 Hooks 的映射 1.旧的生命周期图 2.新的生命周期图 3.新旧生命周期及其与 Hooks 的映射表 二.部分生命周期详解 1.getDerive ...

  9. React组件的生命周期

    react组件的生命周期 文章目录 react组件的生命周期 总览 挂载时 更新时 卸载时 被移除的生命周期 总览 React的生命周期大体可以分为三个部分,一,挂载阶段:二更新阶段三,卸载阶段 挂载 ...

最新文章

  1. 数组洗牌算法-shuffle
  2. python打开csv文件、计算总成绩_实现读取csv文件,文件里面是有限个百分数成绩(99.6、76.8等等...
  3. Observers:让 ZooKeeper更具可伸缩性 | 时光机
  4. docker更新容器命令 ,自启
  5. Android工程Gradle仓库配置及说明
  6. java struts2 上传图片_Java框架Struts2实现图片上传功能
  7. listview item里面的控件点击事件
  8. 目录遍历漏洞和文件读取漏洞的区别
  9. C++两种单例(饿汉式,懒汉式)
  10. mysql添加外键约束的语法_Mysql添加外键约束.
  11. cocos2d-x android游戏使用自己的字体
  12. 如何玩转抖音吸粉引流,老路子新热点照样1000+
  13. 深度学习的可解释性!
  14. 【ValueError: data type <class ‘numpy.object_‘> not inexact】
  15. C++ 使用Poco库实现XML的读取和写入
  16. dockerfile以alpine为基础镜像构建nginx镜像
  17. 再说“恢复被删除的文件”(转)
  18. TFT-LCD液晶显示器的工作原理
  19. 用ps帧动画整个放置身份证示意图
  20. allegro-Could not create new pin inst: PA15/JTDI.

热门文章

  1. IIS7 经典模式和集成模式的区别分析(转载)
  2. 网络协议从入门到底层原理(8)HTTPS(成本、通信过程、TLS1.2的连接,配置服务器HTTPS)
  3. 【微型计算机原理与接口技术】指令概述与标志寄存器
  4. Github部署+Hexo搭建免费博客 next主题美化
  5. vnpy学习10_常见坑
  6. HMM隐马尔可夫模型(HMM)攻略
  7. 手把手教你从0-1做一张酷炫驾驶舱,让老板对你赞不绝口
  8. 都2020年了,你还认为BI=报表?
  9. 查看php项目tp版本,tp5.1如何查看版本号
  10. android 录像 源代码,android安卓视频录制摄像拍摄源码(测试可用)