持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第17天,点击查看活动详情
组件的生命周期(旧)
昨天说了旧版生命周期的三个阶段
-
初始化阶段:由ReactDOM.render()触发初次渲染
- constructor()
- componentWillMount()
- render()
- componentDidMount() ----- 常用于初始化的相关处理逻辑,例如:开启定时器,发送请求,订阅消息等...
-
更新阶段:由组件内部this.setState()或父组件render触发
- shouldComponentUpdate()
- componentWillUpdate()
- render()
- componentDidUpdate()
-
卸载组件:由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount() ----- 常用于销毁组件时进行收尾,例如:关闭定时器,取消订阅等...
组件的生命周期(新)
不知道有没有小伙伴还记得上一篇中有一个警告提示呢?
在项目中,我用的是新版的React,调用生命周期勾子时,控制台给出了这个警告。
在官网的说明中,这样的函数一共有三个:
componentWillMountcomponentWillReceivePropscomponentWillUpdate
这三个生命周期方法经常会被误解和滥用,且预计在一部渲染中,会潜在更大的问题,因此需要将这些生命周期添加"UNSAFE_"前缀。(这里的 “unsafe” 不是指安全性,而是表示使用这些生命周期的代码在 React 的未来版本中更有可能出现 bug,尤其是在启用异步渲染之后。)
// 组件挂载前执行
UNSAFE_componentWillMount() {
console.log('Count: componentWillMount')
}
// 组件更新前执行
UNSAFE_componentWillUpdate() {
console.log('Count: componentWillUpdate')
}
// 父组件调用render执行
UNSAFE_componentWillReceiveProps(){
console.log('Children: componentWillReceiveProps')
}
加上"UNSAFE_"前缀后,可以看到控制台的警告已经没有了:
我们再来聊一聊新版的生命周期吧,先把流程图贴上:
通过与旧版生命周期流程图的对比,我们可以看到,原先旧版挂载时的
componentWillMount不见了,取而代之的是一个新的勾子函数getDerivedStateFromProps。
而针对于更新时,旧版的componentWillReceiveProps和componentWillUpdate两个不见了,新版的多出来getDerivedStateFromProps和getSnapshotBeforeUpdate这两个。
getDerivedStateFromProps
横跨了挂载和更新,我们先来看看它是怎么样的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>react的生命周期(旧)</title>
</head>
<body>
<!-- 测试容器 -->
<div id="test"></div>
<!-- 加载 React 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 加载 React DOM 用于支持 React 操作 DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 加载 babel 用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/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 }
}
getDerivedStateFromProps(){
console.log('Count: getDerivedStateFromProps')
}
// 组件挂载后执行
componentDidMount() {
console.log('Count: componentDidMount')
}
// 控制组件更新阀门
shouldComponentUpdate() {
console.log('Count: shouldComponentUpdate')
return true
// return false
}
// 组件更新后执行
componentDidUpdate() {
console.log('Count: componentDidUpdate')
}
// 挂载组件
render() {
console.log('Count: render')
const { count } = this.state
return (
<div>
<h2>当前求和为:{count}</h2>
<button onClick={this.addOne}>增加 1</button>
<button onClick={this.unmount}>卸载组件</button>
<button onClick={this.force}>强制更新</button>
</div>
)
}
// 按钮回调
addOne = () => {
// 获取原状态
const { count } = this.state
// 更新状态
this.setState({ count: count + 1 })
}
// 卸载按钮回调
unmount = () => {
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
// 强制更新
force = () => {
this.forceUpdate()
}
}
ReactDOM.render(<Count />, document.getElementById("test"))
</script>
</body>
</html>
从运行结果图中可以看到,该勾子函数并没有在控制台中打印出来,反而还给了我们一个异常提示,提示我们说该函数被定义为实例方法,将被忽略,我们需要将其申明为静态方法。
加上 static 再看看效果:
static getDerivedStateFromProps(){
console.log('Count: getDerivedStateFromProps')
}
该函数被执行了,但是又有新的问题出现,提示我们得返回一个有效的状态对象或者null。
static getDerivedStateFromProps(){
console.log('Count: getDerivedStateFromProps')
return null
}
先返回一个null测试看看效果,可以看到正常显示:
再来返回一个状态对象看一下:
static getDerivedStateFromProps(){
console.log('Count: getDerivedStateFromProps')
return {count: 888}
}
可以看到返回了状态对象之后,我们针对state做的更新操作都没受到影响,该状态属性count的值仍旧为: 888。这功能好像没啥用是吧?别急,根据该函数的名字,我们可以联想到其应该是可以获取到props的,给它加上参数看看。
static getDerivedStateFromProps(props){
console.log('Count: getDerivedStateFromProps', props)
return props
}
ReactDOM.render(<Count count={668}/>, document.getElementById("test"))
通过案例可以看到我们通过props得到了一个状态,那我们什么时候才会用到这个方法呢?在官网中已经有给出相关的案例了,我们来看一下:
具体案例可以参考官网给出的相关链接:react.docschina.org/blog/2018/0…
getSnapshotBeforeUpdate
getSnapshotBeforeUpdate() {
console.log('Count: getsnapshotBeforeUpdate')
}
顺序是对的,但是控制台给出了异常的警告提示我们需要有返回值。对于null我们知道,但是快照是什么呢,我们去官网查看下说明:
原来它可以接收两个参数,并将其传递给componentDidUpdate勾子函数使用,我们来试试看:
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log('Count: getsnapshotBeforeUpdate', prevProps, prevState)
return "snapshot"
}
componentDidUpdate(prevProps, prevState, snapshotValue) {
console.log('Count: componentDidUpdate', prevProps, prevState, snapshotValue)
}
图中可以看到componentDidUpdate确实是接收到了getSnapshotBeforeUpdate传递的几个参数。利用getSnapshotBeforeUpdate函数,我们就可以在组件更新之前先保存当前的快照,将其传递给componentDidUpdate,让我们可以在更新之后拿到更新前的值。