开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
前言
脑袋和嘴巴都经常瓢,干脆再整理一下 ····<*)) >>=<。
一、不同版本生命周期
16.3 版本之前
16.3 版本之后
React 16.3 版本:
- 为不安全的生命周期引入别名,
UNSAFE_componentWillMount、UNSAFE_componentWillReceiveProps和UNSAFE_componentWillUpdate。(旧的生命周期名称和新的别名都可以在此版本中使用。) - 引入两个新的生命周期,静态的
getDerivedStateFromProps和getSnapshotBeforeUpdate。
React 16.3 之后的版本:
- 为 componentWillMount、componentWillReceiveProps 和 componentWillUpdate 启用废弃告警。(旧的生命周期名称和新的别名都将在这个版本中工作,但是旧的名称在开发模式下会产生一个警告。)
React 16.9 版本:
- 删除 componentWillMount、componentWillReceiveProps 和 componentWillUpdate。(在此版本之后,只有新的 “UNSAFE_” 生命周期名称可以使用。之后版本“UNSAFE_” 开发模式下会产生一个警告。)
- Deprecate old names for the UNSAFE_* lifecycle methods.
注意:
- UNSAVE_前缀的三个函数,目的是为了向下兼容,但是对于开发者而言应该尽量避免使用它们,而是使用新增的生命周期函数替代它们。
二、三个阶段
挂载阶段:组件即将开始初始化并挂载到 DOM 的阶段。更新阶段:一旦组件被添加到 DOM,它只有在 prop 或 state 状态发生变化时才可能更新和重新渲染。这些只发生在这个阶段。卸载阶段:组件被销毁并从 DOM 中删除。
| 16.3 生命周期 | 特点 |
|---|---|
| 挂载阶段 | |
| constructor | 组件完成 React 数据的初始化。 |
| static getDerivedStateFromProps | 组件初始渲染,调用 render () 之前调用。 |
| render | 组件第一次渲染dom |
| componentDidMount | 组件第一次渲染完成,此时dom节点已经生成。 |
| 更新阶段 | |
| static getDerivedStateFromProps | 组件props更新后触发。 |
| shouldComponentUpdate | 当 props 或 state 发生变化时触发。 |
| render | 重新渲染。 |
| getSnapshotBeforeUpdate | 在render() 之后,componentDidUpdate之前调用。 |
| componentDidUpdate | 在更新后会被立即调用。 |
| 卸载阶段 | |
| componentWillUnmount | 组件卸载和销毁。 |
三、16.3 之后钩子函数
1、挂载阶段
1.1 constructor
代表过程:构造函数,最先被执行;完成 React 数据的初始化。
语法:
- 接收两个参数:
props和context
使用场景:一般用来操作 props 和初始化 state,或者给自定义方法绑定this。
import React from "react";
import moment from "moment";
export default class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
currentYear: moment().year(),
contries:['beijing','shanghai']
};
this.getValue = this.getValue.bind(this);
}
getValue(contryId) {
return this.state.contries[contryId];
}
}
注意:
- 无论有没有 constructor, render 中都可以使用 this.props, 默认自带。
- 如果组件没有声明 constructor, react 会默认添加一个空的 constructor。
- 如果在 constructor 中要使用 this.props, 就必须给 super 加参数: super(props)。
- super 应该在其他语句之前调用。
- 写了 constructor 必须有 super,否则 class 组件会报错
// 报错:
ReferenceError: Must call super constructor in derived class before
accessing 'this' or returning from derived constructor
constructor() {
super();
}
或者
constructor(props) {
super(props);
}
- 在 constructor() 函数中不要调用 setState() 方法,请直接在构造函数中为
this.state 赋值初始化 state。如需在其他方法中赋值,你应使用 this.setState() 替代。
1.2 static getDerivedStateFromProps
代表过程:会再调用 render () 之前调用,并且在初始挂载及后续更新时都会被调用。即每次渲染之前都会触发,后续 props 更新也会触发。
语法:接收更新后的 props、更新前的 state 参数,返回一个对象来更新 state,如果返回 null 则不更新任何内容。
static getDerivedStateFromProps(nextProps, prevState)
nextProps:更新后props
prevState:更新前state
使用场景:如果 props 传入的内容不需要影响到你的 state,那么就需要返回一个 null,这个返回值是必须的,所以尽量将其写到函数的末尾。
static getDerivedStateFromProps(nextProps, prevState) {
const { type } = nextProps;
// 当传入的type发生变化的时候,更新state
if (type !== prevState.type) {
return { type };
}
// 否则,对于state不进行任何操作
return null;
}
注意:
- static 不允许用 this,也就是这个函数不能通过 this 访问到 class 的属性,而是应该通过参数提供的 nextProps 以及 prevState 来进行判断,根据新传入的 props 来映射到 state。
- 由于16.4的修改,这个函数会在每次
re-rendering之前被调用。即使你的 props 没有任何变化,而是父 state 发生了变化,导致子组件发生了 re-render,这个生命周期函数依然会被调用。
1.3 render()
代表过程:初次渲染Dom。
注意:
- render函数是纯函数,只返回需要渲染的东西,不应该包含其它的业务逻辑。
- 返回的类型:
- 原生的 DOM
- React元素:JSX 或者 自定义React组件。
- 数组或者 Fragment
- Portals
- 字符串或数值类型:在DOM中被渲染为文本节点。
- Boolean和null:什么都不渲染。
1.4 componentDidMount
代表过程:会在组件挂载后(插入 DOM 树中)立即调用,即组件第一次渲染完成后会调用,此时dom节点已经生成。
componentDidMount?(): void;
使用场景:组件装载之后调用,此时我们可以获取到DOM节点并操作,比如对 canvas,svg 的操作、服务器请求、监听、订阅都可以写在这个里面,但是记得在 componentWillUnmount 中取消监听、订阅。
注意:
- 可以在这里调用 ajax 请求,返回数据 setState 后组件会重新渲染。但此渲染会发生在浏览器更新屏幕之前。如此保证了即使在 render() 两次调用的情况下,用户也不会看到中间状态。
2、更新阶段
触发时机:当组件的 props 或 state 发生变化时会触发更新。
2.1 shouldComponentUpdate
代表过程:当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染render执行之前被调用。
语法:返回一个布尔值,true表示会触发重新渲染,false表示不会触发重新渲染,默认返回true,我们通常利用此生命周期来优化 React 程序性能。
shouldComponentUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): boolean;
nextProps: 更新后props
nextState: 更新后state
注意:
- 唯一用于控制组件重新渲染的生命周期,由于在react中,setState以后,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新。
- 因为 react 父组件的重新渲染会导致其所有子组件的重新渲染,这个时候其实我们是不需要所有子组件都跟着重新渲染的,因此需要在子组件的该生命周期中做判断。
2.2 render()
代表过程:重新渲染,render函数会插入jsx生成的dom结构,react会生成一份虚拟dom树,在每一次组件更新时,在此react会通过其diff算法比较更新前后的新旧DOM树,比较以后,找到最小的有差异的DOM节点,并重新渲染。
2.3 getSnapshotBeforeUpdate
代表过程:在render() 之后,componentDidUpdate之前调用,react只会在第一次初始化成功会进入componentDidmount,组件更新完毕后,之后每次重新渲染后都会进入getSnapshotBeforeUpdate。
语法:这个函数有一个返回值,会作为第三个参数传给componentDidUpdate。
getSnapshotBeforeUpdate?(prevProps: Readonly<P>, prevState: Readonly<S>): SS | null;
prevProps: 更新前props
prevState: 更新前state
2.4 componentDidUpdate
代表过程:在更新后会被立即调用,首次渲染不会执行此方法。
componentDidUpdate?(prevProps: Readonly<P>, prevState: Readonly<S>, snapshot?: SS): void;
注意:
- 如果 shouldComponentUpdate()返回值为 false,则不会调用 componentDidUpdate()。
- 在 componentDidUpdate 可以使用 this.setSate(),但必须包裹在一个条件语句中,否则会导致死循环。
3、卸载阶段
3.1 componentWillUnmount
代表过程:会在组件卸载及销毁之前直接调用。
componentWillUnmount?(): void;
使用场景:
- 清除组件中所有的
timer:setTimeout, setInterval。 - 取消
网络请求。 - 移除组件中的所有
监听removeEventListener。 - 清除在组件中创建的
订阅。
componentDidMount() {
// 一般在componentDidMount创建监听和订阅
window.addEventListener('scroll', this.xxx);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.xxx);
}
注意:
- componentWillUnmount() 中不应调用 setState() ,因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它。
四、16.3 之前钩子函数
1、挂载阶段
一样的就不复述了。
1.1 componentWillMount(过时)
代表过程:在 constructor() 初始化数据后,render() 之前调用。因此在此方法中同步调用 setState() 不会触发额外渲染,但我们建议使用 constructor() 来初始化 state。
UNSAFE_componentWillMount(): void
注意:
- react16.3 后(包括)使用 UNSAFE_componentWillMount。
- 此方法是服务端渲染唯一会调用的生命周期函数。
2、更新阶段
2.1 componentWillReceiveProps(过时)
代表过程:会在已挂载的组件接收新的 props 之前被调用。
UNSAFE_componentWillReceiveProps(nextProps, nextContext): void
nextProps: 更新后props
注意:
- 16.3 后(包括)使用 UNSAFE_componentWillReceiveProps。
- 如果父组件导致组件重新渲染,即使 props 没有更改,也会调用此方法。
- React 不会针对初始 props 调用 UNSAFE_componentWillReceiveProps()。组件只会在组件的 props 更新时调用此方法。
- 调用 this.setState() 通常不会触发 UNSAFE_componentWillReceiveProps()
2.3 componentWillUpdate(过时)
代表过程:在render之前,shouldComponentUpdate之后,props 和 state 修改都会触发。
UNSAFE_componentWillUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): void;
nextProps: 更新后props
nextState: 更新后state
注意:
- 16.3 后(包括)使用 UNSAFE_componentWillUpdate。
- 如果 shouldComponentUpdate() 返回 false,则不会调用 UNSAFE_componentWillUpdate()。
- 禁止在这里使用 this.setState(),不应该执行任何操作触发对 react组件的更新。
五、父子组件生命周期
加载顺序:
挂载时:子组件的挂载钩子先被触发。
卸载时:子组件的卸载钩子后被触发。
1、父子组件初始化
Parent: constructor
Parent: getDerivedStateFromProps
Parent: render
children: constructor
children: getDerivedStateFromProps
children: render
children: componentDidMount
Parent: componentDidMount
2、父子组件卸载
Parent: componentWillUnmount
children: componentWillUnmount