一、看一下官方文档的说法

总结一下:

  • 为了后续的版本和异步渲染等操作,有三个生命周期函数不被推荐使用,在后续版本中会被废除。
  • 分别是componentWillMount()、componentWillUpdate()、componentWillReceiveProps()、
  • 按照官方文档说法
    • 在16.3版本以后,我们在这样去写会报警告
    • 在17.0版本,会删除这三个生命周期钩子(还没被删除,但是也要避免以前的写法)
    • 我们使用17.0版本的react.js文件来看一下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<body>
<!-- 创建一个容器 -->
<div id="test"></div>
<!-- 引入核心库 -->
<script src="/react/js/17.0.1版本/react.development.js"></script>
<!-- 引入react-dom 用于支持react操作dom -->
<script src="/react/js/17.0.1版本/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转为js -->
<script src="/react/js/17.0.1版本/babel.min.js"></script>
<!-- 注意这里的类型 为babel -->
<script type="text/babel">
//创建类式组件
class Count extends React.Component {
constructor(props) {
console.log('Count --- constructor');
super(props)
this.state = { count: 0 }
}
add = () => {
let { count } = this.state
count += 1
this.setState({ count })
}
force = () => {
this.forceUpdate()
}
//组件将要挂载的钩子
componentWillMount() {
console.log('Count --- componentWillMount');
}
//组件挂载完毕的钩子
componentDidMount() {
console.log('Count --- componentDidMount');

}
//控制组件更新的阀门
shouldComponentUpdate() {
console.log('Count --- shouldComponentUpdate');
//不写这个钩子 默认返回true 写了这个钩子一定自己返回个值
//true代表允许更新,继续往下执行 false代表不允许更新,不会进行后续的操作
return false
}
//组件将要更新的钩子
componentWillUpdate() {
console.log('Count --- componentillUpdate');
}
//组件更新完毕的钩子
componentDidUpdate() {
console.log('Count --- componentDidUpdate');
}
//初次渲染,状态更新时调用
render() {
console.log('Count --- render');
const count = this.state
return (
<div>
<h2>求和:{this.state.count}</h2>
<button onClick={this.add}>+1</button>
<button onClick={this.force}>不更改状态,强制更新</button>
</div>
)
}
}
class A extends React.Component {
state = { carName: '大奔' }
changeCarName = () => {
this.setState({ carName: '奥迪' })
}
render() {
return (
<div>
<h2>我是A组件</h2>
<button onClick={this.changeCarName}>换车</button>
{/*子组件B*/}
<B carName={this.state.carName}/>
</div>
)
}

}
class B extends React.Component {
//组件将要接收新的props的钩子 可以传递值
componentWillReceiveProps(props) {
console.log('B --- componentWillReceiveProps',props);
}
//初次渲染,状态更新时调用
render() {
return (
<div>
<h2>B组件,接收到的车:{this.props.carName}</h2>
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Count />, document.getElementById('test'))
</script>

</body>

可以看到,我是引入了新版本也就是react17.0.1的版本

但是控制台中只是提出了警告,没有报错,说明还可以用但不推荐,而且以后可能会移除

我们加上前缀UNSAFE_

1
2
3
4
5
6
7
8
9
10
11
12
//组件将要挂载的钩子
UNSAFE_componentWillMount() {
console.log('Count --- componentWillMount');
}
//组件将要更新的钩子
UNSAFE_componentWillUpdate() {
console.log('Count --- componentillUpdate');
}
//组件将要接收新的props的钩子 可以传递值
UNSAFE_componentWillReceiveProps(props) {
console.log('B --- componentWillReceiveProps', props);
}

可以看到,不报警告了

我这里渲染的是Count组件,UNSAFE_componentWillReceiveProps()这个钩子没有用到,我就不掩饰了,一样的道理。

来看一下新版的生命周期钩子

总结下:

新版的舍弃了三个钩子上面提到那三个

新增了两个钩子。getDerivedStateFromProps()、getSnapshotBeforeUpdate()(实际开发中很少用到)

二、getDerivedStateFromProps()

直译:得到派生的状态从props中

先看代码吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<script type="text/babel">
//创建类式组件
class Count extends React.Component {
constructor(props) {
console.log('Count --- constructor');
super(props)
this.state = { count: 0 }
}
add = () => {
let { count } = this.state
count += 1
this.setState({ count })
}
force = () => {
this.forceUpdate()
}
getDerivedStateFromProps() {
console.log('Count --- getDerivedStateFromProps');
}
//组件挂载完毕的钩子
componentDidMount() {
console.log('Count --- componentDidMount');

}
//控制组件更新的阀门
shouldComponentUpdate() {
console.log('Count --- shouldComponentUpdate');
//不写这个钩子 默认返回true 写了这个钩子一定自己返回个值
//true代表允许更新,继续往下执行 false代表不允许更新,不会进行后续的操作
return true
}
//组件更新完毕的钩子
componentDidUpdate() {
console.log('Count --- componentDidUpdate');
}
//初次渲染,状态更新时调用
render() {
console.log('Count --- render');
const count = this.state
return (
<div>
<h2>求和:{this.state.count}</h2>
<button onClick={this.add}>+1</button>
<button onClick={this.force}>不更改状态,强制更新</button>
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Count />, document.getElementById('test'))
</script>

按照上面的新生命钩子图,顺序应该是constructor,getDerivedStateFromProps,render,componentDidMount

看下输出

报错:把这个方法放在实例上会被忽略,应该用static标识为类上的方法

1
2
3
static getDerivedStateFromProps() {
console.log('Count --- getDerivedStateFromProps');
}

在看输出:

mark

报错解释:应该返回一个状态对象或者null

返回null试试

返回一个状态对象:

1
2
3
4
static getDerivedStateFromProps() {
console.log('Count --- getDerivedStateFromProps');
return {count: 100}
}

注意此时 我们修改状态+1,数值100是不变化的,而且不再是默认的数值1

上面我们提到,可以从props中获取值:

1
2
3
4
5
static getDerivedStateFromProps(props) {
console.log('Count --- getDerivedStateFromProps',props);
return props
}
ReactDOM.render(<Count count={1000}/>, document.getElementById('test'))

看下输出:这里的返回的是props,且改变状态值页面上不会变化,因为依赖的是props,peops不变,页面自然不变

这个钩子用的情况较特殊:state的值在任何时候都取决于props这个情况下才会用到,其他情况很少用到。

这个钩子可以传递两个参数:

1
2
3
4
static getDerivedStateFromProps(props,state) {
console.log('Count --- getDerivedStateFromProps',props,state);
return props
}

state是初始状态值,props是返回的状态对象。

至于为什么不能去修改状态值:因为这个钩子横跨挂载阶段和更新阶段,任何值都要受到传过来的props影响

若state的值在任何时候都取决于props,可以使用它

三、getSnapshotBeforeUpdate()

直译:得到快照在更新之前

看下代码:

1
2
3
4
5
6
7
static getDerivedStateFromProps(props,state) {
console.log('Count --- getDerivedStateFromProps',props,state);
return props
}
getSnapshotBeforeUpdate() {
console.log('Count --- getSnapshotBeforeUpdate');
}

初始页面打印效果:

但我们点击页面+1后

页面上没有改变,因为受props影响

初始state值改变 变为1001了

getSnapshotBeforeUpdate()钩子报错,让我们返回一个快照值或null

null就不说了,什么是快照值?任何类型都可以,可以返回Number,String等等

我们先来看一个钩子 componentDidUpdate:

1
2
3
4
5
6
7
8
9
10
11
12
//便于演示,把这俩钩子先注释掉
//static getDerivedStateFromProps(props, state) {
// console.log('Count --- getDerivedStateFromProps', props, state);
// return props
// }
// getSnapshotBeforeUpdate() {
// console.log('Count --- getSnapshotBeforeUpdate');
// }
//组件更新完毕的钩子 我在这里传递了两个参数,看下输出了什么
componentDidUpdate(a, b) {
console.log('Count --- componentDidUpdate', a, b);
}

初始页面:

点击+1后:

可以看到 打印出来的是先前的状态值:

组件更新完成调用这个钩子,返回的是没更新之前的值

+1后 count变为1,但打印的是count:0

两个参数都是先前的值

a,b具体含义:preProps,preState及之前的props和state

1
2
3
4
//组件更新完毕的钩子
componentDidUpdate(preProps, preState) {
console.log('Count --- componentDidUpdate', preProps, preState);
}

继续上个问题,快照值我们知道可以返回,但是返回后交给谁了?

看下官方文档

可以看到此方法的任何返回值都作为参数传递给componentDidUdate()

1
2
3
4
5
6
7
getSnapshotBeforeUpdate(preProps, preState) {
console.log('Count --- getSnapshotBeforeUpdate',preProps,preState);
return '测试值'
}
componentDidUpdate(preProps, preState,snapshot) {
console.log('Count --- componentDidUpdate', preProps, preState,snapshot);
}

初始页面打印:

+1后:

可以看到 componentDidMount第三个值就是传递过来的快照值

这里大家可能有疑问getDerivedStateFromProps的打印值是1000,1001,而下面两个钩子打印值都是1000,1000

因为下面两个钩子都是更新前的状态值,而getDerivedStateFromProps是更新后的状态值,自然不一样

至于为什么第一个参数都是打印的1000,因为我们传递的props就是count:1000,不会因为+1而改变。

注意:这个钩子也不常用

四、getSnapshotBeforeUpdate()的使用场景

一个内容框不断新增内容,可以上滑下滚动查看,不滚动是内容区域信息不变,新增内容在scroll范围内不显示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<script type="text/babel">
//创建类式组件
class List extends React.Component {
state = { newsArr: [] }
componentDidMount() {
setInterval(() => {
const { newsArr } = this.state

const news = '新闻' + (newsArr.length + 1)

this.setState({ newsArr: [news, ...newsArr] })
},1000)
}
getSnapshotBeforeUpdate() {
return this.refs.list.scrollHeight
}
componentDidUpdate(height) {
this.refs.list.scroll += this.refs.scrollHeihght - height
}
render() {
return (
<div ref="list">

{this.state.newsArr.map((n,index) => {
return <div key={index}>{n}</div>
})}

</div>
)
}
}
ReactDOM.render(<List />, document.getElementById('test'))
</script>

五、新生命周期总结

初始化阶段

由ReactDOM.render()触发 —— 初次渲染

  • constructor() —— 类组件中的构造函数
  • static getDerivedStateFromProps(props, state) 从props得到一个派生的状态【新增】
  • render() —— 挂载组件
  • componentDidMount() —— 组件挂载完成 比较常用

更新阶段

由组件内部this.setSate()或父组件重新render触发或强制更新forceUpdate()

  • getDerivedStateFromProps() —— 从props得到一个派生的状态 【新增】
  • shouldComponentUpdate() —— 组件是否应该被更新(默认返回true)
  • render() —— 挂载组件
  • getSnapshotBeforeUpdate() —— 在更新之前获取快照【新增】
  • componentDidUpdate(prevProps, prevState, snapshotValue) —— 组件完成更新

卸载组件

  • 由ReactDOM.unmountComponentAtNode()触发
  • componentWillUnmount() —— 组件即将卸载

重要的勾子

  • render:初始化渲染或更新渲染调用
  • componentDidMount:开启监听, 发送ajax请求
  • componentWillUnmount:做一些收尾工作, 如: 清理定时器

即将废弃的勾子

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate
  • 现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。