每个组件都包含生命周期方法,在运行过程中的特定的阶段主动执行。我们可以重写这些方法,以便在不同阶段做相应的操作。
组件的生命周期可分为三个阶段: 挂载阶段 / 更新阶段 / 卸载阶段。
挂载阶段
constructor()
static getDerivedStateFromProps(props, state)
componentWillMount()
(v16.3不安全的)render()
componentDidMount()
1. constructor()
- 在 React 组件挂在之前调用
- 用于初始化 state ,进行方法绑定
2. static getDerivedStateFromProps(props, state)
- 会在调用
render()
之前调用,并且在初始挂载及后续更新中都会调用。应返回一个对象来更新 state
3. componentWillMount()
- v16.3不安全的,可转换,v17将不可用,如果使用了新API,再使用不安全的API会报错
- 组件被挂载DOM前调用1次,
setState()
不会引起组件渲染
4. render()
- 组件唯一必须方法;
- 根据组件的 props 与 state 的变化返回 React 元素,用于描述组件的UI
render()
应为纯函数,如需修改 state,需在其它生命周期如componentDidMount()
中进行操作- 如果更新阶段的
shouldComponentUpdate()
返回false
,则不会调用render()
5. componentDidMount()
- 会在组件已经被渲染到DOM之后运行,只执行一次,可在其中使用
this.setState()
使组件重新渲染,可在此向后端请求数据
更新阶段
当组件的 state 和 props 发生变化时,组件将重新渲染,或者调用 forceUpdate()
时,也会触发 render()
重新渲染,更新 UI
static getDerivedStateFromProps(props, state)
(同挂载阶段2)componentWillReceiveProps(nextProps)
(v16.3不安全的)shouldComponentUpdate(nextProps, nextState)
componentWillUpdate()
(v16.3不安全的)render()
(同挂载阶段4)getSnapshotBeforeUpdate(prevProps, prevState)
componentDidUpdate(prevProps, prevState, snapshot)
2. componentWillReceiveProps(nextProps)
- v16.3不安全的,可转换,v17将不可用
- 在已挂载的组件接收新的 props 之前调用,只在更新阶段触发
- 只 props 改变触发,state 改变不触发
- 如果需要更新状态以响应 props 变化,通过比较
this.props
和nextprops
是否相同,而后使用this.setState()
执行 state 转换。
3. shouldComponentUpdate(nextProps)
- 当 state 或 props 发生变化,在渲染之前调用
- 通过返回
false
来告知 React 跳过更新 - 若返回
false
则不会再触发componentWillUpdate
、render
和componentDidUpdate
方法
4. componentWillUpdate()
- v16.3不安全的,可转换,v17将不可用
- 当 state 或 props 发生变化,在渲染之前调用,只在更新阶段触发
- 不能使用
this.setState()
方法,或其他会触发更新的方法 - 尽量使用
componentDidUpdate()
替代此方法
6. getSnapshotBeforeUpdate(prevProps, prevState)
- 不常用
- 在最近一次渲染输出之前调用,在组件改变之前从 DOM 中获取一些信息并作为返回值返回
- 返回值将传递给
componentDidUpdate()
作为第三个参数,如果无返回值则为undefind
7. componentDidUpdate(prevProps, prevState, snapshot)
- 组件更新后立即调用,只在更新阶段触发
- 可操作 DOM 或进行网络请求
- 可以使用
this.setState()
方法,但必须在 if 语句中,以避免死循环
卸载阶段
componentWillUnmount()
1. componentWillUnmount()
- 当组件从 DOM 中移除时,会调用此方法
- 再次方法中执行必要的清理操作,如清除 timer ,取消网络请求等
- 不应使用
this.setState()
方法,因为组件不会再进行渲染
PS:应当尽量避免使用不安全的 API,且避免共用新 API 和不安全的 API。
示例代码
包含大部分生命周期钩子函数,为避免共用新旧 API 导致的报错,已将新 API 注释。
import React, { Component } from 'react';
class LifecycleIn extends Component {
// 挂载阶段
constructor(props) {
super(props);
this.state = {date: new Date()}
console.log('---- 挂载阶段 start ----')
console.log('constructor');
}
componentWillMount() {
console.log('componentWillMount')
}
// static getDerivedStateFromProps() {
// console.log('getDerivedStateFromProps');
// return null;
// }
componentDidMount() {
// 挂载时的生命周期钩子
console.log('componentDidMount');
console.log('---- 挂载阶段 end ----');
}
// 更新阶段
componentWillReceiveProps() {
console.log('---- 更新阶段 start ----')
console.log('componentWillReceiveProps');
}
shouldComponentUpdate() {
console.log('shouldComponentUpdate');
return true;
}
componentWillUpdate() {
console.log('componentWillUpdate');
}
// getSnapshotBeforeUpdate() {
// console.log('getSnapshotBeforeUpdate');
// return 'getSnapshotBeforeUpdate';
// }
componentDidUpdate(prevProps, prevState, snapshot) {
console.log(`componentDidUpdate - ${snapshot}`);
console.log('---- 更新阶段 end ----');
}
componentWillUnmount() {
// 组件卸载时的生命周期钩子
console.log('---- 卸载阶段 end ----');
console.log('componentWillUnmount');
console.log('---- 卸载阶段 end ----');
}
tick = () => {
console.log('---- 更新阶段 start ----')
this.setState({
date: new Date()
});
}
render() {
console.log('render');
return (
<div>
<button onClick={this.tick}>修改state</button>
<p>Hello {this.props.name}!</p>
<p>{this.state.date.toLocaleTimeString()}</p>
</div>
);
}
}
export default class Lifecycle extends Component {
state = {
name: 'World',
}
changeLifecycleName = () => {
this.setState({
name: this.state.name == 'React' ? 'World' : 'React',
})
}
render() {
return (
<div>
<button onClick={this.changeLifecycleName}>修改props</button>
<LifecycleIn name={this.state.name} />
</div>
)
}
}