React组件生命周期

2023-11-12

原文地址

每个组件都包含“生命周期方法”,我们可以重写这些方法,以便于在运行过程中特定的阶段执行这些方法。

生命周期图谱

React生命周期图谱
React官方提出了三个特定阶段:挂载阶段、更新阶段、卸载阶段。

挂载阶段

  • constructor()
  • static getDerivedStateFormProps()
  • render()
  • componentDidMount()

constructor()

在React组件挂载之前,会调用它的constructor(构造函数),是最先被执行的函数,在constructor中通过调用super(props)来获取外部传递过来的数据。通常,构造函数仅用于两种情况:

  • 通过给this.state赋值对象来初始化内部state
  • 为事件处理函数绑定实例(为自定义方法绑定this

static getDerivedStateFromProps()

getDerivedStateFromProps会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。

简单来说这个生命周期函数的作用就是让我们可以将传入组件的props映射到state上。
一个简单的例子:
有一个App组件,维护一个num变量,定义一个函数可以给num变量加一,绑定给一个按钮,并将变量num传递给子组件Mount

function App() {
  const [num, setNum] = useState(0)
	
  const changeNum = () => {
    setNum(v=>v+1)
  }
	
  return (
    <div className="App">
      <button onClick={changeNum}>外部改变</button>
      <Mount num={num}/>
    </div>
  )
}

有一个Mount组件,维护一个自身num变量初始化为父组件num值,该组件也有一个改变num的函数绑定了一个按钮,并且展示props.numstate.num

class Mount extends Component {
  constructor(props) {
    super(props)
    this.state = {
      num: props.num,
    }
  }

  changeNum = () => {
    this.setState({
      num: this.state.num + 1
    })
  }

  render() {
    return (
      <div>
        <div>state: {this.state.num}</div>
        <div>props: {this.props.num}</div>
        <button onClick={this.changeNum}>内部改变</button>
      </div>
    )
  }
}

现在当我们点击父组件按钮时,子组件的props.num发生了变化,点击子组件按钮时子组件的state.num也会变化。
图2图3

现在我们需要实现一个当点击父组件按钮,子组件state.num也要和父组件props.num一起变化的效果,就可以用到getDerivedStateFromProps生命周期函数。

只需要给子组件添加如下代码:

static getDerivedStateFromProps(nextProps, prevState) {
    if (nextProps.num !== prevState.num) {
      return {
        num: nextProps.num
      }
    }
    return null
}

该生命周期函数接收两个参数,根据以上参数名就可见名知意。

值得一说的是,getDerivedStateFromProps的执行时间是每次触发render之前,所以如果添加以上函数,就会造成一个现象,即就是再点击子组件的按钮state.num将无法更新。

因为setState()也会导致getDerivedStateFromProps执行,而此时setState已经改变了state.num,所以也就导致了生命周期函数中的if()条件是成立的,结果就是state.num又被重新赋值成了之前的props.num

要解决这一个’BUG’,其实只需要保存setState执行之前的state.num的值,然后让if()中的判断不再是和state.num比较,而是和state.pervNum比较,如下:

constructor(props) {
	super(props)
	this.state = {
		num: props.num,
		prevNum: props.num
	}
}
static getDerivedStateFromProps(nextProps, prevState) {
	if (nextProps.num !== prevState.prevNum) {
		return {
			num: nextProps.num,
			prevNum: nextProps.num
		}
	}
	return null
}

这样每当触发setState时,就不会影响if()的判断了,也就促使了子组件的setState不受该生命周期的影响。

render()

render() 方法是 class 组件中唯一必须实现的方法。
当 render 被调用时,它会检查 this.props 和 this.state 的变化并返回以下类型之一:

  • React元素
  • 数组或fragments
  • Portals
  • 字符串或数值类型
  • 布尔类型或null

render() 函数应该为纯函数,这意味着在不修改组件 state 的情况下,每次调用时都返回相同的结果,并且它不会直接与浏览器交互。

componentDidMount()

componentDidMount() 会在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据,此处是实例化请求的好地方。

简单来说就是在组件初始化渲染之后立刻调用一次,且在整个组件的生命中只调用一次。

这应该是最常用的生命周期函数之一,通常用来进行实例化网络请求、添加订阅、添加事件监听等等。

更新阶段

  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()

shouldComponentUpdate()

该函数接收两个参数:shouldComponentUpdate(nextProps, nextState)

该函数的返回值将直接影响到组件的重新渲染(默认情况下返回true,返回false时将告知React跳过更新),我们可以使用这两个参数和自写逻辑去控制组件的渲染。

所以该函数是在组件重新渲染即render()被调用之前执行的。

值得一说的是,该函数独属于更新阶段,并且forceUpdate()是不会调用该方法的。

getSnapshotBeforeUpdate()

getSnapshotBeforeUpdate(pervProps, pervState) 在最近一次渲染输出(提交到 DOM 节点)之前调用。

在此方法中我们可以访问更新前的propsstate,并且必须和componentDidUpdate()一起使用,因为此方法的返回值将作为componentDidUpdate()的第三个参数。(ps. 如果使用了该生命周期,则返回值不能为undefined,否则会报错)

这个生命周期执行的时机非常的微妙:
图4

它是处于render()之后,DOM更新之前。在此处获取的DOM状态依然是之前的DOM状态,所以此生命周期经常被用来做一些与DOM相关的操作(例子)。

componentDidUpdate()

componentDidUpdate() 会在更新后会被立即调用。首次渲染不会执行此方法。

任何导致组件重新渲染的情况都会让该生命周期函数执行,例如:setState()forceUpdate()、新的props

这里我们可获取三个参数componentDidUpdate(prevProps, prevState, snapshot)
利用前两个参数,我们可以对更新前后的propsstate进行比较,以便做出响应,比如说某种条件下进行网络请求或不进行请求,或者进一步对state做出更改。

值得一说的是,在这个函数中调用setState()必须要在条件语句中,而不能直接暴露出来。

因为一旦直接暴露出来,setState()的直接执行,又会导致该生命周期函数执行,进而陷入死循环,所以在此处使用setState()一定要慎之又慎。

如果组件实现了 getSnapshotBeforeUpdate() 生命周期(不常用),则它的返回值将作为 componentDidUpdate() 的第三个参数 “snapshot” 参数传递。否则此参数将为 undefined。

卸载阶段

  • componentWillUnmount()

componentWillUnmount()

componentWillUnmount() 会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等。
componentWillUnmount() 中不应调用 setState(),因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它。

:本博客仅供个人学习,如有错误、侵权敬请指出。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

React组件生命周期 的相关文章

随机推荐

  • 数值分析复习笔记-第七章-非线性方程求根

    Chapter7 非线性方程求根 7 1 前言 本质 对一些n次代数多项式or超越方程 它们的根是难以通过解析方法求得 因此需采取数值方法 主要有 二分法 不动点迭代法 迭代加速 牛顿迭代法 牛顿法 割线法 7 2 二分法 数学基础 零点定
  • 多边形等分算法

    多边形等分
  • Python基础-- 9函数(中)

    一 函数的返回值 返回值就是函数执行以后返回的结果 通过return来指定函数的返回值 return后面可以跟任意对象 返回值甚至可以是 个函数 二 文档字符串 在定义函数时 可以在函数内部编写文档字符串 文档字符串就是对函数的 说明 he
  • Linux中TAB补全显示设备空间不足问题

    今天在使用linux中习惯性的使用tab键进行补全信息 发现无论在何处按下tab都会显示这样报错 图中因为我用了xshell工具 这里报错是中文的 这个时候要检查下自己的空间是否充足 可使用df查看设备目录使用空间大小 可以将一下不用的目录
  • 概率论的几种常考分布总结

    两点分布 0 1分布 X b 1 p 二项分布 X b n p k 0 1 2 n 指数分布 参数为 线性分布 参数为a b 泊松分布 X k 0 1 2 n
  • 程序员常犯的5个非技术性错误

    一个好的软件开发人员需要培养两种技能 技术技能和非技术技能 不幸的是一些开发者只注重技术的部分 以致养成一些陋习 下面是最常犯的5个非技术性错误 0 缺乏自律 Jim Rohn曾经说过 自律是目标和成果之间的桥梁 我一直认为 不论是成为一名
  • 半导体芯片测试介绍:CP、FT、STL

    CP测试 Chip Probing 对Wafer进行电性能测试 挑选出好的Die 可以减少封装和测试的成本 也可以透过Wafer的良率 检查fab厂制造的工艺水平 FT测试 Final Test 芯片封装完成之后 通过分选机和测试机的配合使
  • Android 兼容8.0 App全局字体调节、禁止App字体随系统字体大小而更改

    在APP中 字体的大小单位一般会用sp 然而在改变系统字体大小时 App字体就会随着系统字体大小改变而改变 这就可能造成APP布局的错位 造成这种情况的原因是 sp单位除了受屏幕密度影响外 还受到用户的字体大小影响 通常情况下 建议使用sp
  • 【周末闲谈】什么是云计算?

    个人主页 个人主页 系列专栏 周末闲谈 第一周 二进制VS三进制 第二周 文心一言 模仿还是超越 第二周 畅想AR 文章目录 前言 什么是云计算 大数据 云计算是分布式计算的一种 云计算为我们提供的三种服务 基础设置即服务 laaS 软件运
  • Unicode汉字编码表

    1 Unicode编码表 Unicode只有一个字符集 中 日 韩的三种文字占用了Unicode中0x3000到0x9FFF的部分 Unicode目前普遍采用的是UCS 2 它用两个字节来编码一个字符 比如汉字 经 的编码是0x7ECF 注
  • 几种任务调度的 Java 实现方法与比较

    I 几种任务调度的 Java 实现方法与比较 综观目前的 Web 应用 多数应用都具备任务调度的功能 本文由浅入深介绍了几种任务调度的 Java 实现方法 包括 Timer Scheduler Quartz 以及 JCron Tab 并对其
  • project facets java转成web项目

    前言 用Eclipse开发项目的时候 把一个Web项目导入到Eclipse里会变成了一个Java工程 将无法在Tomcat中进行部署运行 方法 1 找到 project文件 找到里面的
  • [GUI]stm32搭载3.5寸SPI-TFT屏移植LittleVGL

    唠几句 记录下移植笔记 新项目用到LVGL 也是首次接触GUI库 所以Emmmm 学呗 之前都是直接在LCD屏上画点 画线 画圆 画个矩形 画个多边形 显示个字符串 显示张图片而已 没有用过GUI库 在网上找了点学习资料 然后把LVGL库的
  • Springboot企业级部署解决方案

    使用springboot的童鞋们 有没曾经想把项目打包成 bin conf libs logs 等这样的结构然后直接运行的 但是找了很多办法都不够完美 因为G是个完美主义 好了直接来看解决方案 1 修改执行打包的子工程的pom xml文件
  • Emojify - v2参考答案

    Emojify Welcome to the second assignment of Week 2 You are going to use word vector representations to build an Emojifie
  • Android允许EditText获取焦点,但是不弹出系统键盘的方法

    1 以下方法可以避免弹出系统软键盘 但是scanPayDialog里面的editText也失去了焦点 scanPayDialog getWindow setSoftInputMode WindowManager LayoutParams S
  • RK3568 Debian10 固态硬盘自动挂载

    目录 需求 实现 步骤 附录 Example Field definitions 参考资料 Platform RK3568 OS Debian10 Kernel v4 19 219 Module Wdxsky SSD NVME 950 12
  • 基于js+echarts实现数据可视化大屏展示

    vue echarts大屏数据可视化展示点击进入 写在前面 本项目中使用的是echarts图表库 ECharts 提供了常规的折线图 柱状图 散点图 饼图 K线图 用于统计的盒形图 用于地理数据可视化的地图 热力图 线图 用于关系数据可视化
  • 收藏: 浅析SPDK技术和知识点

    固态存储设备正在取代数据中心 如今新一代的闪存存储 比起传统的磁盘设备 在性能 功耗和机架密度上具有显著的优势 这些现有的优势将会继续增大 使闪存作为下一代存储设备进入市场 用户使用现在的固态设备 比如 Intel SSD DC P4600
  • React组件生命周期

    原文地址 每个组件都包含 生命周期方法 我们可以重写这些方法 以便于在运行过程中特定的阶段执行这些方法 生命周期图谱 React官方提出了三个特定阶段 挂载阶段 更新阶段 卸载阶段 挂载阶段 constructor static getDe