React生命周期

所谓的React生命周期,就是指组件从被创建出来,到被使用,最后被销毁的这么一个过程;

而在这个过程中,React提供了我们会自动执行的不同的钩子函数,我们称之为生命周期函数;

**组件的生命周期大致分为三个阶段:组件挂载阶段,组件更新阶段,组件销毁卸载阶段 **

16.4之后的声明周期图谱如下:

组件初始化阶段

由ReactDOM.render()触发—初次渲染

  1. constructor()
  2. getDerivedStateFromProps
  3. render()
  4. componentDidMount()

组件更新阶段

由组件内部this.setState()或者父组件重新render触发或者forceUpdate()

  1. getDerivedStateFromProps
  2. shouldComponentUpdate()
  3. render()
  4. getSnapshotBeforeUpdate()
  5. componentDidUpdate()

组件卸载阶段

由ReactDOM.unmountComponentAtNode()触发

  1. componentWillUnmount()

React 15 生命周期和 React 16.3 生命周期在挂载阶段的主要差异在于,废弃了 componentWillMount,新增了 getDerivedStateFromProps废弃了 componentWillUpdate() 新增了 getSnapshotBeforeUpdate

而在16以前的声明周期图谱如下:

初始化阶段

由ReactDOM.render()触发—初次渲染
1.constructor()
2.componentWillMount()
3.render()
4.componentDidMount()

更新阶段

由组件内部this.setSate()或父组件重新render触发
1.shouldComponentUpdate()
2.componentWillUpdate()
3.render()
4.componentDidUpdate()

卸载组件

由ReactDOM.unmountComponentAtNode()触发
1.componentWillUnmount()

下面展开来说说组件的生命周期阶段

组件初始化(挂载)阶段

挂载过程在组件的一生中仅会发生一次,在这个过程中,组件被初始化,然后会被渲染到真实 DOM 里,完成所谓的“首次渲染”;

我们的App组件继承了react Component这个基类,才能使用render(),生命周期等方法;

super(props)用来调用基类的构造方法( constructor() ), 也将父组件的props注入给子组件;

constructor 方法,该方法仅仅在挂载的时候被调用一次,我们可以在该方法中对 this.state 进行初始化;

其中componentWillMount 会在执行 render 方法前被触发,一些同学习惯在这个方法里做一些初始化的操作,但这些操作往往会伴随一些风险或者说不必要性;

接下来 render 方法被触发。注意 render 在执行过程中并不会去操作真实 DOM(也就是说不会渲染),它的职能是把需要渲染的内容返回出来。真实 DOM 的渲染工作,在挂载阶段是由 ReactDOM.render 来承接的;

componentDidMount 方法在渲染结束后被触发,此时因为真实 DOM 已经挂载到了页面上,我们可以在这个生命周期里执行真实 DOM 相关的操作。此外,类似于异步请求、数据初始化这样的操作也大可以放在这个生命周期来做;

import React, { Component } from 'react';class App extends Component {constructor(props) {super(props);}componentWillMount(){console.log('componentWillMount 执行了')}render() {console.log('render函数执行了')return (<div><h3>生命周期-组件的挂载</h3></div>)}componentDidMount() {console.log('组件已经挂载')}
}

组件更新阶段

组件的更新分为两种:一种是由父组件更新触发的更新;另一种是组件自身调用自己的 setState 触发的更新;

根据生命周期图谱,父组件触发的更新和组件自身的更新相比,多出了这样一个生命周期方法componentWillReceiveProps (nextProps)

在这个生命周期方法里,nextProps 表示的是接收到新 props 内容,而现有的 props (相对于 nextProps 的“旧 props”)我们可以通过

this.props 拿到,由此便能够感知到 props 的变化;

class About extends Component{render(){return(<h3>About</h3>)}componentDidUpdate(){console.log('about 更新')}// 组件被卸载componentWillUnmount(){console.log('about 被卸载了')}componentWillReceiveProps(nextProps){console.log('父组件触发的 About 更新')}
}

shouldComponentUpdate方法

shouldComponentUpdate(nextProps, nextState)

根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。默认行为是 state 每次发生变化组件都会重新渲染。大部分情况下,你应该遵循默认行为。

当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。返回值默认为 true。首次渲染或使用 forceUpdate() 时不会调用该方法。

此方法仅作为性能优化的方式而存在。不要企图依靠此方法来“阻止”渲染,因为这可能会产生 bug。你应该考虑使用内置的 PureComponent 组件,而不是手动编写 shouldComponentUpdate()PureComponent 会对 props 和 state 进行浅层比较,并减少了跳过必要更新的可能性。

如果你一定要手动编写此函数,可以将 this.propsnextProps 以及 this.statenextState 进行比较,并返回 false 以告知 React 可以跳过更新。请注意,返回 false 并不会阻止子组件在 state 更改时重新渲染。

我们不建议在 shouldComponentUpdate() 中进行深层比较或使用 JSON.stringify()。这样非常影响效率,且会损害性能。

static getDerivedStateFromProps不是componentWillMount的替代品

getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新

state,如果返回 null 则不更新任何内容;

getDerivedStateFromProps 这个 API,其设计的初衷不是试图替换掉 componentWillMount,而是试图替换掉

componentWillReceiveProps,因此它有且仅有一个用途:即 state 的值在任何时候都取决于 props

static getDerivedStateFromProps(props, state)
  • getDerivedStateFromProps 是一个静态方法。静态方法不依赖组件实例而存在,因此你在这个方法内部访问不到 this
  • 该方法可以接收两个参数:propsstate,它们分别代表当前组件接收到的来自父组件的 props 和当前组件自身的 state,如果需要修改state的值可以直接像state.count = 10 这样使用;
  • getDerivedStateFromProps 需要一个对象格式的返回值;

React 团队为了确保 getDerivedStateFromProps 这个生命周期的纯洁性,直接从命名层面约束了它的用途。所以,如果你不是出于这个目的来使用 getDerivedStateFromProps,原则上来说都是不符合规范的。

值得一提的是,getDerivedStateFromProps 在更新和挂载两个阶段都会“出镜”(这点不同于仅在更新阶段出现的 componentWillReceiveProps)。这是因为“派生 state”这种诉求不仅在 props 更新时存在,在 props 初始化的时候也是存在的。React 16 以提供特定生命周期的形式,对这类诉求提供了更直接的支持。

消失的 componentWillUpdate 与新增的 getSnapshotBeforeUpdate

这个方法和 getDerivedStateFromProps 颇有几分神似,它们都强调了“我需要一个返回值”这回事。区别在于该函数的返回值会作为第三个参数给到 componentDidUpdate它的执行时机是在 render 方法之后,真实 DOM 更新之前。在这个阶段里,我们可以同时获取到更新前的真实 DOM 和更新前后的 state&props 信息(例如,滚动位置)

class ScrollingList extends React.Component {constructor(props) {super(props);this.listRef = React.createRef();}getSnapshotBeforeUpdate(prevProps, prevState) {// 我们是否在 list 中添加新的 items ?// 捕获滚动​​位置以便我们稍后调整滚动位置。if (prevProps.list.length < this.props.list.length) {const list = this.listRef.current;return list.scrollHeight - list.scrollTop;}return null;}componentDidUpdate(prevProps, prevState, snapshot) {// 如果我们 snapshot 有值,说明我们刚刚添加了新的 items,// 调整滚动位置使得这些新 items 不会将旧的 items 推出视图。//(这里的 snapshot 是 getSnapshotBeforeUpdate 的返回值)if (snapshot !== null) {const list = this.listRef.current;list.scrollTop = list.scrollHeight - snapshot;}}render() {return (<div ref={this.listRef}>{/* ...contents... */}</div>);}
}

React生命周期为什么做如此改变?

在 Fiber 机制下,render 阶段是允许暂停、终止和重启的。当一个任务执行到一半被打断后,下一次渲染线程抢回主动权时,这个任务被重启的形式是“重复执行一遍整个任务”而非“接着上次执行到的那行代码往下走”。这就导致 render 阶段的生命周期都是有可能被重复执行的

带着这个结论,我们再来看看 React 16 打算废弃的是哪些生命周期:

  • componentWillMount;

  • componentWillUpdate;

  • componentWillReceiveProps。

这些生命周期的共性,就是它们都处于 render 阶段,都可能重复被执行,而且由于这些 API 常年被滥用,它们在重复执行的过程中都存在着不可小觑的风险。

别的不说,说说我自己在团队 code review 中见过的“骚操作”吧。在“componentWill”开头的生命周期里,你习惯于做的事情可能包括但不限于:

  • setState();

  • fetch 发起异步请求;

  • 操作真实 DOM。

这些操作的问题(或不必要性)包括但不限于以下 3 点:

(1)完全可以转移到其他生命周期(尤其是 componentDidxxx)里去做

比如在 componentWillMount 里发起异步请求。很多同学因为太年轻,以为这样做就可以让异步请求回来得“早一点”,从而避免首次渲染白屏。

可惜你忘了,异步请求再怎么快也快不过(React 15 下)同步的生命周期。componentWillMount 结束后,render 会迅速地被触发,所以说首次渲染依然会在数据返回之前执行。这样做不仅没有达到你预想的目的,还会导致服务端渲染场景下的冗余请求等额外问题,得不偿失。

(2)在 Fiber 带来的异步渲染机制下,可能会导致非常严重的 Bug

试想,假如你在 componentWillxxx 里发起了一个付款请求。由于 render 阶段里的生命周期都可以重复执行,在 componentWillxxx 被打断 + 重启多次后,就会发出多个付款请求。

比如说,这件商品单价只要 10 块钱,用户也只点击了一次付款。但实际却可能因为 componentWillxxx 被打断 + 重启多次而多次调用付款接口,最终付了 50 块钱;又或者你可能会习惯在 componentWillReceiveProps 里操作 DOM(比如说删除符合某个特征的元素),那么 componentWillReceiveProps 若是执行了两次,你可能就会一口气删掉两个符合该特征的元素。

结合上面的分析,我们再去思考 getDerivedStateFromProps 为何会在设计层面直接被约束为一个触碰不到 this 的静态方法,其背后的原因也就更加充分了——避免开发者触碰 this,就是在避免各种危险的骚操作。

(3)即使你没有开启异步,React 15 下也有不少人能把自己“玩死”。

比如在 componentWillReceiveProps 和 componentWillUpdate 里滥用 setState 导致重复渲染死循环的。

总的来说,React 16 改造生命周期的主要动机是为了配合 Fiber 架构带来的异步渲染机制。在这个改造的过程中,React 团队精益求精,针对生命周期中长期被滥用的部分推行了具有强制性的最佳实践。这一系列的工作做下来,首先是确保了 Fiber 机制下数据和视图的安全性,同时也确保了生命周期方法的行为更加纯粹、可控、可预测

React生命周期详解相关推荐

  1. ES6中的React生命周期详解

    太长时间没写react了,有点生.重新捡起来练练手. import React,{ Component } from 'react';class Demo extends Component {con ...

  2. 1.react生命周期详解(2020.12.05)

    刚敲完vue的生命周期,现在来敲敲react的生命周期,当然生命周期只针对类组件(我很不常用类组件). raect生命周期(v16之前) initialization(初始化阶段) mounting( ...

  3. react 生命周期详解

    React-ES6 : class App extends React.Component {static get propTypes() {return {//nameProp: PropTypes ...

  4. React 生命周期详解(新)

    React 16之后有三个生命周期被废弃: componentWillMount componentWillReceiveProps componentWillUpdate 因为这些生命周期方法容易被 ...

  5. taro生命周期详解

    taro生命周期详解 taro介绍 生命周期 react的钩子函数 为兼容小程序的钩子函数 个别生命周期详解以及注意 1.render()函数 2.constructor()构造函数 3.在各个生命周 ...

  6. Fragment生命周期详解

    关于Fragment的生命周期,博主写过Activity与Fragment生命周期详解,基本上把Fragment的生命周期详细介绍过,但是那仅仅是创建一个Fragmnet时的生命周期,而事实上Frag ...

  7. Android面试之Activity生命周期详解

    Activity生命周期详解 一 Activity的四种状态: Activity的生命周期中存在四种基本的状态:活动状态(Active/Runing),暂停状态(Paused),停止状态(Stoppe ...

  8. Fragment的懒加载与生命周期详解

    提示:本文仅为笔者学习记录 Fragment的懒加载与生命周期详解 什么是懒加载 了解Fragment的生命周期 onAttach onCreate onCreateView onActivityCr ...

  9. vue 声明周期函数_Vue 生命周期详解

    Vue 生命周期详解 Vue 生命周期流程 最开始,用户使用 new Vue() 创建根 Vue 实例,或者 Vue 实例化子组件都会调用_init方法(我们将这两种实例都称为vm): functio ...

  10. 01.软件项目管理与敏捷方法——敏捷项目生命周期详解笔记

    01.软件项目管理与敏捷方法--敏捷项目生命周期详解笔记 00.与其说是船还不如说是熟练的航行技术保证了成功的航行.--George William Curtis 01.敏捷项目是一个按照敏捷宣言宗旨 ...

最新文章

  1. AAAI 2020 | 多模态基准指导的生成式多模态自动文摘
  2. 【分享】(性能优化)思考数据列表中“特殊的列”
  3. keybd_event 被 SendInput 替代
  4. centos 安装指定版本gc_番外篇 (1) Docker 安装
  5. 【Socket网络编程】15. 发送端和接收端数据大小不一致时 的分析
  6. 成功解决AttributeError: module 'torch.utils' has no attribute 'data'
  7. Qt之QByteArray类学习
  8. oracle自带调优,oracle 参数调优
  9. 计算机不能进入桌面,电脑开机无法进入桌面,请高手解决。
  10. java扑克牌移动_蓝桥杯-扑克牌移动-java
  11. 单页面登录——编码传参(oa会对#号会进行截断)
  12. 运筹学 matlab实现运输问题(表上作业法)
  13. [ffmpeg][goav]ffmpeg代码例子pcmu重采样并转码aac格式
  14. matlab实现A律13折线的编码和译码以及量化误差的计算
  15. e3是合法浮点数吗_下列哪些是不合法的浮点数的选项是 123 2e4.2 .e5 -e3 .234 ......
  16. 我的世界java版gamemode指令_我的世界(电脑Java版)简单又好玩指令教程
  17. HSSFSheet设置Excel打印区 横向打印
  18. 笔记本麦克风声音小解决办法
  19. 啊哈C语言 第五章 【代码】【习题答案】
  20. 4.2.7-packet-tracer---configure-router-on-a-stick-inter-vlan-routing

热门文章

  1. 网站建设教程:如何自己做网站,步骤有哪些?
  2. 单火开关和零火开关的区别
  3. 35岁 计算机 学 什么好,35岁一事无成, 想重新学习, 应该学习哪方面的技能?
  4. 教育技术学专业属于计算机类么,教育技术学是什么专业
  5. 手把手教你调整电脑磁盘的分区大小
  6. oracle文本类型字段,Oracle字符的5种类型的介绍
  7. 程序员如何成为架构师
  8. android开发利器--站在巨人肩膀上前行
  9. python将.tif格式图批量转化为.jpg格式图
  10. 如何计算十五个字节(多字节)的CRC16校验