React生命周期详解
# 前言
目前React
的类组件依旧是我日常开发构建组件的主要方式,尽管React-Hooks
一直被认为是未来React
发展趋势,了解Class
组件内置的生命周期函数对后续过渡到Hooks
时代也是有帮助的。
本篇博客需要记录的问题有:
- react 各个生命周期函数的用法。
- 新增的React16.4新增的函数以及用法。
# 生命周期函数的分类与作用
生命周期函数大致可以分为3类阶段:
- 挂载阶段
- 更新阶段
- 卸载阶段
下面开始分阶段介绍:
# 1.挂载阶段
当组件实例
被创建并插入 DOM 中时,其生命周期调用顺序如下:
constructor()
构造器这个阶段
constructor
函数只解决两件事:- 通过
this.state
赋值对象来初始化内部state
。 - 为事件处理函数绑定实例(ps:虽然这个要多写点代码,但是貌似性能不错)
- 通过
static getDerivedStateFromProps()
- 在初始化挂载及后续更新时都会被调用。
- 返回一个对象来更新
state
- 应用:
- 父组件渲染会导致子组件渲染,可以在此生命周期函数对数据进行拦截
render()
- class 组件中唯一必须实现的方法
- 支持返回的参数:
JSX
语法null
orfalse
componentDidMount()
- 会在组件实例挂载后(插入 DOM 树中)立即调用
- 应用:
- 可以在此生命周期发起网络请求。
# constructor
constructor(props) {
super(props);
// 不要在这里调用 this.setState()
this.state = { counter: 0 };
this.handleClick = this.handleClick.bind(this);
}
// 下面这中写法貌似也是可以的
// state 直接作为类组件的一个属性。
class App extends React.Component{
state = { counter: 0}
}
2
3
4
5
6
7
8
9
10
11
12
如果没有写
constructor
函数,会有一个默认的构造函数,所以可以不显式定义如果要在
constructor
函数中使用this
指针,一定需要super(props)
# static getDerivedStateFromProps
static getDerivedStateFromProps(nextProps, prevState,snapshot) {
// 根据nextProps和prevState计算出预期的状态改变,返回结果会被送给setState
// 注:这里无法使用 this 指针
}
2
3
4
该函数是一个纯函数:纯函数的意思就是说输出完全决定于输入。
(nextProps,prevState)
newState
此函数用于替换掉大部分的
render
前的生命周期函数,包括:componentWillReceiveProps
componentWillMount
注:在此生命周期中发起
AJAX
请求是无法赶在首次render
之前拿到数据结果的。componentWillUpdate
替换原因:
Reactv17
会采用Fiber
,render
函数之前的所有函数都有可能被执行多次。
# componentDidMount
可以在此阶段,使用AJAX
请求数据
componentDidMount(){
axios.post('https://web-api.juejin.im/v3/web/wbbr/bgeda')
.then((res)=>{console.log('axios 获取数据成功:'+JSON.stringify(res)) })
.catch((error)=>{console.log('axios 获取数据失败'+error)})
}
2
3
4
5
# 2.更新阶段
当组件实例
被创建并插入 DOM 后时,其生命周期调用顺序如下:
static getDerivedStateFromProps()
- 在初始化挂载及后续更新时都会被调用。
- 返回一个对象来更新
state
- 应用:
- 父组件渲染会导致子组件渲染,可以在此生命周期函数对数据进行拦截
shouldComponentUpdate
- 在此生命周期,可以优化渲染性能。
- 默认
return true
,可以return false
停止渲染
render()
- class 组件中唯一必须实现的方法
- 支持返回的参数:
JSX
语法null
orfalse
getSnapshotBeforUpdate()
- 没啥用,可以处理视图层相关的操作。
componentDidUpdate()
- 可以解决
setState(,callbakc)
- 可以解决
# static getDerivedStateFromProps
由于React
更新后,render
前的方案都需要被反复调用,所以无论在挂载阶段,还是在更新阶段,此函数都会执行。
使用方法见:点击跳转
# 🚨componentWillReceiveProps
非常常用的一个控制状态更新的生命周期函数,在旧版React
这样的生命周期函数只有两个:
shouldComponentUpdate
:只能return true
或者false
componentWillReceiveProps
:通过比较两个
props
:nextProps
(新)和this.props
(旧,因为还没有render),可以直接对state
状态进行更新,并且注意的是由于是在render
函数之前,所以这种更新是同步的
。state = { a: 0 } componentWillReceiveProps(nextProps){ this.setState({a:1}) console.log(this.state.a) // 0 } render(){ console.log(this.state.a) // 1 }
1
2
3
4
5
6
7
8
9
10
11注:这样一来会破坏掉
state
数据的==单一数据源==的特性,导致组件状态变得不可测。
# shouldComponentUpdate
在React中,当prop
或者state
发生变化时,可以通过在shouldComponentUpdate
生命周期函数中执行return false
来阻止页面的更新,从而减少不必要的render执行。
shouldComponentUpdate(nextProps,nextState){
// 判断:this.props与nextProps | this.state与nextState
// 注:不要使用JSON.stringfy()进行深比较,非常影响效率、损耗性能。
if( 条件判断 ){
return false
}
return true
}
2
3
4
5
6
7
8
技巧:如果使用pureComponent
会自动对状态进行判断
class App extends React.pureComponent{
}
2
3
# render
没啥好说的,
# getSnapshotBeforeUpdate
见componentDidUpdate()
:点击跳转
# componentDidUpdate
可以用于替换setState(,callback)
函数,setState
对状态修改后,会进入到componentDidUpdate
这个生命周期函数。
state = {
a: 0,
b: 0
}
// 会刷新3次
this.setState({a:1},()=>{
this.setState({b:1},()=>{
this.setState({c:1})
})
})
// 使用componentDidUpdate替换写法
// !如果在此生命周期中,使用this.setState()会引发循环渲染
// 所以必须有条件判断
componentDidUpdate(nextProps,nextState){
if(nextState.a ===1){
this.setState({b:1})
}
if(nexState.a ===1 && nextState.b===1){
this.setState({c:1})
}
}
// 可以发现这种写法很low,这个时候需要重新思考数据的设计模式。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
点击查看
除此以外,getDerivedStateFromProps
还可以与getSnapshotBeforeUpdate
配合使用,后者的值可以作为getDerivedStateFromProps
的第三个参数输入:
getSnapshotBeforeUpdate(prevProps, prevState) {
// snapshot咋看是组件级别的某个“快照”,其实返回任何值
// 官方推荐:在此阶段可以获取DOM元素,但是具体的有效实例没有发现
// 程墨更是直接不推荐使用
return 'foo';
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('#enter componentDidUpdate snapshot = ', snapshot);
}
2
3
4
5
6
7
8
9
10
# 3.卸载阶段
卸载阶段只有一个生命周期函数,componentWillUnmount()
会在组件卸载及销毁之前直接调用。
# componentWillUnmount
在此方法中执行必要的清理操作:
- 清除 timer,取消网络请求或清除
- 取消在 componentDidMount() 中创建的订阅等
注:不应该再这个方法中使用
setState
,因为组件一旦被卸载,就不会再装载,也就不会重新渲染。
# 不常用的知识点
# 1.state|props初始化
state
的初始化函数:constructor
props
的初始化函数:defaultProps
这个牵扯到一个知识点,React声明类组件实际上有两种方式:
ES5原生方式
React.createClass
定义的组件。var ShowTitle = React.createClass({ getDefaultProps:function(){ return{ title : "React" } }, getInitialState:function(){ return { title : "React" } } render : function(){ return <h1>{this.props.title}</h1> } });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15🚨 目前这种写法已经不推荐了。
ES6形式,通过继承
React.component
的方式定义的组件,也是目前React官方最为推荐的写法。class App extends React.Component { // defaultProps 作为静态属性 static defaultProps = { title: "react" } constructor(props){ super(props) this.state = {} } } // 别的教程中,对defaultProps配合 prop-types 使用 App.defaultProps = { defaultValue : "Hello" }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 图解生命周期
# v16.4
图片资源来源于React官网:https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
注:16.3 React 修复了getDerivedStateFromProps 在setState()不更新的Bug。
# v15
还有一张知乎程墨的图:主要就是props
改变后多走了一步componentWillReceiveProps