react 生命周期钩子详解(1)
本文章使用react
环境版本为17.0.1,文章中出现的 (已废弃)等标识皆对react 17+版本而言
React中组件也有生命周期,也就是说也有很多钩子函数供我们使用
1、早期的react生命周期钩子
根据下图我们可以将react
的生命周期钩子分为四个阶段:初始化、挂载阶段、更新阶段,销毁阶段
(1)初始化
初始化时期的生命周期钩子是constructor
只要这个组件被实例化,被挂载的时候,这个钩子就会被优先调用,他有一个参数 pros
可以调用 super(props)
,用来将父组件传来的 pros
绑定到这个类中,作用是状态初始化和属性读取,这个钩子只会在初始化时被调用一次。
constructor
中应当做些初始化的动作,如:初始化 state
,将事件处理函数绑定到类实例上,但也不要使用 setState()
。如果没有必要初始化 state
或绑定方法,则不需要构造 constructor
,或者把这个组件换成纯函数写法。
当然也可以利用 props
初始化 state
,在之后修改 state
不会对 props
造成任何修改,但仍然建议大家提升状态到父组件中,或使用 redux
统一进行状态管理。
import React, { Component } from 'react'
export default class App extends Component {
//初始化期钩子,只要这个组件被实例化被挂载的时候,这个钩子就会被优先调用
//参数props
//作用:状态初始化和属性读取
constructor(pros){
//调用super把props递到父组件上去
super(pros)
// 在状态初始化时读取了从父组件上传来的props
this.state={
title:pros.title
}
console.log("constructor")
}
//我们这里用 componentDidMount 钩子来验证 constructor 这个钩子只会被调用一次
componentDidMount(){
// 在这里修改数据 constructor 钩子也不会被再次调用
this.setState({
title:"hello"
})
console.log("componentDidMount")
}
render() {
return (
<div>
你好
</div>
)
}
}
打印结果:
(2)挂载阶段
本阶段有3个生命周期钩子分别是 componentWillMount(已废弃) 、render 、componentDidMount
1)componentWillMount ( UNSAFE_componentWillMount )
这个钩子是在进入挂载阶段时期第一个运行,在 react 17.x版本之后已废弃
在组件挂载前被调用,在这个方法中调用 setState()
不会起作用,是由于他在 render()
前被调用。
这个方法因为是在渲染之前被调用,也是惟一一个可以直接同步修改 state
的地方。
import React, { Component } from 'react'
export default class App extends Component {
constructor(){
super()
console.log("constructor")
}
componentWillMount(){
console.log("componentWillMount")
}
componentDidMount(){
console.log("componentDidMount")
}
render() {
console.log("render")
return (
<div>
你好
</div>
)
}
}
使用时会如下图所示
并提示我们可以使用 UNSAFE_componentWillMount 代替,用来消除黄色的警告信息,而在react 18.x的版本中将会将所有弃用的生命周期重命名为它们的新名称
import React, { Component } from 'react'
export default class App extends Component {
constructor(){
super()
console.log("constructor")
}
UNSAFE_componentWillMount(){
console.log("UNSAFE_componentWillReceiveProps")
}
componentDidMount(){
console.log("componentDidMount")
}
render() {
console.log("render")
return (
<div>
你好
</div>
)
}
}
在这个钩子中可以拿到 this
,也能够利用 setState()
更改 state
状态,不过尽量不要在此进行 state
状态的更改,可能会引起一些以外的错误,尽量在 constoructor
中直接设置好,在这个钩子中更改state
状态不会两次调用 render
,因为他是在 render 之前就将 state 更改了。它与 constoructor
一样,如果在后面更改 state
状态,他不会再次被调用,只会被调用一次
UNSAFE_componentWillMount(){
console.log(this)
console.log("UNSAFE_componentWillReceiveProps")
}
2)render
render()方法是必需的。当他被调用时,他将计算this.props
和this.state
,并返回以下一种类型:
- React元素。通过jsx创建,既可以是dom元素,也可以是用户自定义的组件。
- 字符串或数字。他们将会以文本节点形式渲染到dom中。
- Portals。react 16版本中提出的新的解决方案,可以使组件脱离父组件层级直接挂载在DOM树的任何位置。
- null,什么也不渲染
- 布尔值。也是什么都不渲染。
当返回null
,false
,ReactDOM.findDOMNode(this)
将会返回null,什么都不会渲染。
render()
方法必须是一个纯函数,他不应该改变state
,也不能直接和浏览器进行交互,应该将事件放在其他生命周期函数中。
如果shouldComponentUpdate()
返回false
,render()
不会被调用。
3)componentDidMount
在组件被装配后立即调用。初始化使得DOM节点应该进行到这里。
通常在这里进行ajax请求
如果要初始化第三方的dom库,也在这里进行初始化。只有到这里才能获取到真实的dom.
如果我们在这个函数中更改 state 状态,会在此执行 render 函数
import React, { Component } from 'react'
export default class App extends Component {
constructor(){
super()
console.log("constructor")
}
// componentWillMount(){
// console.log("componentWillMount")
// }
UNSAFE_componentWillMount(){
console.log(this)
console.log("UNSAFE_componentWillReceiveProps")
}
componentDidMount(){
console.log("componentDidMount")
this.setState({
title:"hello"
})
}
render() {
console.log("render")
return (
<div>
你好
</div>
)
}
}
第一次 render 是在组件挂载是被调用,第二次被调用是在更改 state 状态之后
(3)更新阶段
更新阶段分为对 pros 的更新和对 state 的更新,他们之间的区别仅仅是 pros 更新多了一个 componentWillReceiveProps 钩子
1) componentWillReceiveProps(UNSAFE_componentWillReceiveProps(nextProps))
它与之前的 componentWillMount 一样被废弃,可以使用UNSAFE_componentWillReceiveProps(nextProps)
当组件挂载后,接收到新的props
后会被调用。如果需要更新state
来响应props
的更改,则可以进行this.props
和nextProps
的比较,并在此方法中使用this.setState()
。
如果父组件会让这个组件重新渲染,即使props
没有改变,也会调用这个方法。
React不会在组件初始化props时调用这个方法。调用this.setState
也不会触发。
2) shouldComponentUpdate(nextProps, nextState)
调用shouldComponentUpdate
使React知道,组件的输出是否受state
和props
的影响。默认每个状态的更改都会重新渲染,大多数情况下应该保持这个默认行为。
在渲染新的props
或state
前,shouldComponentUpdate
会被调用。默认为true
。这个方法不会在初始化时被调用,也不会在forceUpdate()
时被调用。返回false
不会阻止子组件在state
更改时重新渲染。
如果shouldComponentUpdate()
返回false
,componentWillUpdate
,render
和componentDidUpdate
不会被调用。
官方并不建议在shouldComponentUpdate()
中进行深度查询或使用JSON.stringify()
,他效率非常低,并且损伤性能。
3)componentWillUpdate(UNSAFE_componentWillUpdate(nextProps, nextState)))
它与之前的 componentWillMount 一样被废弃,可以使用UNSAFE_componentWillUpdate(nextProps, nextState)
在渲染新的state
或props
时,UNSAFE_componentWillUpdate
会被调用,将此作为在更新发生之前进行准备的机会。这个方法不会在初始化时被调用。
不能在这里使用this.setState(),也不能做会触发视图更新的操作。如果需要更新state
或props
,调用getDerivedStateFromProps
。
4)render
见上述
5)componentDidUpdate(prevProps, prevState, snapshot)
在更新发生后立即调用componentDidUpdate()
。此方法不用于初始渲染。当组件更新时,将此作为一个机会来操作DOM。只要您将当前的props与以前的props进行比较(例如,如果props没有改变,则可能不需要网络请求),这也是做网络请求的好地方。
如果组件实现getSnapshotBeforeUpdate()
生命周期,则它返回的值将作为第三个“快照”参数传递给componentDidUpdate()
。否则,这个参数是undefined
。
(4) 卸载阶段
componentWillUnmount()
在组件被卸载并销毁之前立即被调用。在此方法中执行任何必要的清理,例如使定时器无效,取消网络请求或清理在componentDidMount
中创建的任何监听。