基础-生命周期
1. 概览
v16 版本之前的生命周期:

React 的生命周期主要包括三个阶段: 初始化阶段, 运行中阶段和销毁阶段, 在 React 不同的生命周期里, 会一次触发不同的钩子函数.
2. 初始化阶段
- 设置组件的默认属性
static defaultProps = {
name: 'sls',
age:23
};
//or
Counter.defaltProps={name:'sls'}
- 设置组件的初始化状态
constructor() {
super();
this.state = {number: 0}
}
componentWillMount(): 组件即将被渲染到页面之前触发,此时可以进行开启定时器、向服务器发送请求等操作render(): 组件渲染componentDidMount(): 组件已经被渲染到页面中后触发:此时页面中有了真正的 DOM 的元素,可以进行 DOM 相关的操作
3. 运行中阶段
componentWillReceiveProps(): 组件接收到属性时触发shouldComponentUpdate(): 当组件接收到新属性,或者组件的状态发生改变时触发。组件首次渲染时并不会触发
shouldComponentUpdate(newProps, newState) {
if (newProps.number < 5) return true;
return false
}
//该钩子函数可以接收到两个参数,新的属性和状态,返回true/false来控制组件是否需要更新。
我们可以通过该函数来优化性能.
一个 React 项目需要更新一个小组件时,很可能需要父组件更新自己的状态。而一个父组件的重新更新会造成它旗下所有的子组件重新执行 render()方法,形成新的虚拟 DOM,再用 diff 算法对新旧虚拟 DOM 进行结构和属性的比较,决定组件是否需要重新渲染.
无疑这样的操作会造成很多的性能浪费,所以我们开发者可以根据项目的业务逻辑,在shouldComponentUpdate()中加入条件判断,从而优化性能.
例如React中的就提供了一个PureComponent的类,当我们的组件继承于它时,组件更新时就会默认先比较新旧属性和状态,从而决定组件是否更新。值得注意的是,PureComponent进行的是浅比较,所以组件状态或属性改变时,都需要返回一个新的对象或数组.
componentWillUpdate(): 组件即将被更新时触发componentDidUpdate(): 组件被更新完成后触发。页面中产生了新的 DOM 的元素,可以进行 DOM 操作
4. 销毁阶段
componentWillUnmount(): 组件被销毁时触发。这里我们可以进行一些清理操作,例如清理定时器,取消 Redux 的订阅事件等等。
5. V16 的生命周期
概览如下:

6. 变更缘由
原来的生命周期在 v16 退出 Fiber 之后就不合适了, 因为如果要开启async rendering, 在render函数之前的所有函数, 都可能被执行多次.
原来这些函数都是在render之前调用:
- componentWillMount
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
如果开发者使用了async rendering, 而且又在以上这些 render 前执行的生命周期做 ajax 请求的话, 那 ajax 将被无谓的多次调用. 而且componentWillMount里发起的 ajax 不管多快也赶不上首次的render, 而且componentWillMount在服务器端渲染也会被调用到(当然,也许这是预期的结果),这样的 IO 操作放在componentDidMount 里更合适。
除了shouldComponentUpdate, 其他的在render函数之前的所有函数(componentWillMount,componentWillReceiveProps,componentWillUpdate)都将被getDerivedStateFromProps替代
也就是用一个静态函数getDerivedStateFromProps来取代被 deprecate 的几个生命周期函数,就是强制开发者在render之前只能做无副作用的操作,而且能做的操作局限在根据props和state决定新的state
7. 新的钩子函数
7.1 static getDerivedStateFromProps(props, state)
在组件创建时和更新时的 render 方法之前调用,它应该返回一个对象来更新状态,或者返回 null 来不更新任何内容。
7.2 getSnapshotBeforeUpdate
getSnapshotBeforeUpdate() 被调用于render之后,可以读取但无法使用 DOM 的时候。它使您的组件可以在可能更改之前从 DOM 捕获一些信息(例如滚动位置)。此生命周期返回的任何值都将作为参数传递给componentDidUpdate()。
例子:
class ScrollingList extends React.Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
}
getSnapshotBeforeUpdate(prevProps, prevState) {
//我们是否要添加新的 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>;
}
}
8. 简易用法一览
class ExampleComponent extends React.Component {
// 用于初始化 state
constructor() {}
// 用于替换 `componentWillReceiveProps` ,该函数会在初始化和 `update` 时被调用
// 因为该函数是静态函数,所以取不到 `this`
// 如果需要对比 `prevProps` 需要单独在 `state` 中维护
static getDerivedStateFromProps(nextProps, prevState) {}
// 判断是否需要更新组件,多用于组件性能优化
shouldComponentUpdate(nextProps, nextState) {}
// 组件挂载后调用
// 可以在该函数中进行请求或者订阅
componentDidMount() {}
// 用于获得最新的 DOM 数据
getSnapshotBeforeUpdate() {}
// 组件即将销毁
// 可以在此处移除订阅,定时器等等
componentWillUnmount() {}
// 组件销毁后调用
componentDidUnMount() {}
// 组件更新后调用
componentDidUpdate() {}
// 渲染组件函数
render() {}
// 以下函数不建议使用
UNSAFE_componentWillMount() {}
UNSAFE_componentWillUpdate(nextProps, nextState) {}
UNSAFE_componentWillReceiveProps(nextProps) {}
}