React生命周期
我们这里所说的生命周期是基于类式组件的,其中主要分为三个阶段
- 挂载
- 更新
- 卸载
以上每一个阶段内都有自己的一系列在当前生命周期中可被自动执行的生命周期函数(类似于vue的钩子函数)
挂载阶段(初始化)
在挂载阶段按照执行顺序分别有以下几个生命周期函数
-
constructor()
初始化整个组件的state 以及调用super(props),该方法只会执行一次 -
componentWillMount()
挂载之前(已经弃用,目前React18.2中依然还是可以使用,如果使用会报警告) -
render()
只返回需要渲染页面结构,不要包含其它的业务逻辑 -
componentDidMount()
挂载之后,可以获取到DOM节点并操作,服务器请求,启用事件监听,调用 setState()等都可以在此函数中完成,该方法只会执行一次
在组件代码中表示如下
import React, { Component } from 'react'
export default class Mount extends Component {
//挂载阶段第一个执行的生命周期函数
constructor(props){
super(props)
console.log("1、初始化")
}
//挂载阶段第二个执行的生命周期函数
componentWillMount(){
console.log("2、挂载之前")
}
//挂载阶段第三个执行的生命周期函数
render() {
console.log("3、渲染页面结构")
return (
<div>
</div>
)
}
//挂载阶段第四个执行的生命周期函数
componentDidMount(){
console.log("4、挂载之后")
}
}
注意事项:
在执行过程中我们会发现如下情况
1、控制台会报一个警告,这个警告主要是告诉我们关于componentWillMount函数的名称在React18.x的版本中名字要做修改,如下
UNSAFE_componentWillMount(){ console.log("2、挂载之前") }
2、按照上面的方式修改之后又会又一个警告,告诉我们在严格模式下使用 UNSAFE_ 的写法可以会导致提示代码中存在错误,这个可以把在入口文件index.js中设置的React.StrictMode删除掉,就不会有警告了
3、如果在严格模式下,我们会发现我们的生命周期函数出了componentWillMount之外的其他三个全部都执行了两边,把严格模式删除掉即可
扩展内容:为什么会渲染两次?
之所以这些生命周期函数会被渲染两次的原因在于React.StrictMode严格模式所提供的好处之一,它帮助我们检测生命周期函数在执行渲染时的副作用,而严格模式下的检测方式就是故意调用一些关键函数两次,来帮助发现副作用
这种渲染两次的行为会对性能造成一定影响,但是它只针对开发环境,在生产环境中不会发生渲染两次的情况
更新阶段
更新阶段分为两种情况,分别是props更新时和state更新时,我们先来看state更新时会执行的生命周期函数
state更新时按照执行顺序会触发如下函数:
-
shouldComponentUpdate()
根据return的布尔值来决定是否开始更新数据 -
componentWillUpdate()
准备开始更新 -
render()
重新渲染页面结构 -
componentDidUpdate()
更新之后
注意:
componentWillReceiveProps()
方法会把新更新props接收为参数- 如果
shouldComponentUpdate()
方法return的是false,则后面三个函数不会执行,同时,这个方法可以接收两个参数,分别是更新之后的state数据和更新之后的props数据
代码演示:
import React, { Component } from 'react'
class Son extends Component {
state = {
str:"hoho"
}
componentWillReceiveProps(newProps){
console.log("0、当接收到新的props",newProps)
}
shouldComponentUpdate(newProps,newState){
console.log("1、是否更新数据",newProps,newState);
return true
}
componentWillUpdate(){
console.log("2、准备开始更新")
}
render(){
console.log("3、重新渲染页面结构")
return (
<div>
<button onClick={this.changeStr.bind(this)}>更新state</button>
</div>
)
}
componentDidUpdate(){
console.log("4、更新之后")
}
//制作一个修改state的方法触发生命周期的更新阶段
changeStr(){
this.setState({
str:"haha"
})
}
}
export default class Mount extends Component {
state = {
num:1
}
//修改父组件传入子组件的num触发componentWillReceiveProps
changeNum(){
this.setState({
num:1
})
}
render(){
return (
<div>
<button onClick={this.changeNum.bind(this)}>修改num</button>
<Son num={this.state.num}></Son>
</div>
)
}
}
注意事项:
componentWillReceiveProps()
函数会报警告要替换成新的static getDerivedStateFromProps()
或者加上UNSAFE_前缀componentWillUpdate()
函数也会遇到上面挂载阶段函数名修改的警告提示,把该函数的名称也加上UNSAFE_ 前缀即可UNSAFE_componentWillUpdate(){ console.log("2、准备开始更新") }
数据修改过程做性能优化
在上面的 shouldComponentUpdate()
函数中,我们知道它会被自动注入两个参数,分别是修改后的props与修改后的state,而是否真的去做数据更新会根据这个函数的return值来决定,这里我们就可以手写一个业务逻辑来对程序进行性能优化
业务逻辑如下:
如果修改之后的state值与修改之前的state值是一样的,那我们就没有必要专门再把数据更新一遍,因为这样做会重新执行一遍render渲染页面结构
把上面的业务逻辑转换成代码表示
shouldComponentUpdate(newProps,newState){
//newState接收的是一个state对象,修改的新值在对象的属性中
if(newState.str === this.state.str){
return false
}else{
return true
}
}
上面的代码过于冗余,做如下修改
shouldComponentUpdate(newProps,newState){
return newState.str !== this.state.str
}
props更新时会触发以下生命周期函数执行
props更新时,所执行的生命周期函数与执行顺序和state更新时一样,只不过会在最前面添加一个生命周期函数的执行
-
componentWillReceiveProps()
外部传入的数据发生变化时触发(父组件修改了一个自己组件内部的数据,而这个被修改的数据时有传递给子组件的) -
shouldComponentUpdate()
根据return的布尔值来决定是否接收外部传入的新值 -
componentWillUpdate()
准备开始更新 -
render()
重新渲染页面结构 -
componentDidUpdate()
更新之后
**注意:**与上面的state更新时一样,当外部传入的数据改变的时候,也会根据
shouldComponentUpdate
返回的布尔值来决定是否要接收在外部已经被修改的新数据,如果return false则后面三个函数不执行
创建组件进行代码演示:
import React, { Component } from 'react'
class Child extends Component {
UNSAFE_componentWillReceiveProps(){
console.log("0、外部传入的props被修改")
}
shouldComponentUpdate(newProps,newState){
console.log("1、是否要更新数据")
return true
}
UNSAFE_componentWillUpdate(){
console.log("2、准备开始更新")
}
render(){
console.log("3、重新渲染页面结构")
return (
<div>
<h1>{this.props.cstr}</h1>
</div>
)
}
componentDidUpdate(){
console.log("4、更新之后")
}
}
//制作一个父组件
export default class Father extends Component {
state = {
fstr:"father"
}
changeStr(){
this.setState({
fstr:"haha"
})
}
render() {
return (
<div>
<button onClick={this.changeStr.bind(this)}>修改父组件的fstr</button>
<Child cstr={this.state.fstr}></Child>
</div>
)
}
}
把props的修改也考虑到数据修改的性能优化上
如果props修改之后的值与修改之前的一样,那么就不执行更新
shouldComponentUpdate(newProps,newState){
return (newState.str !== this.state.str) || (newProps.cstr !== this.props.cstr)
}
PureComponent
PureComponent是React15.3版本新添加的一个继承自Component的子类,当我们的类组件继承自PureComponent的时候,该组件会自动加载shouldComponentUpdate声明周期函数,当组件更新的时候会自动对组件的props和state进行新旧比较,如果没有发生变化就不会触发render方法让组件二次渲染,就从可以达到和上面我们手动编写的优化方法一样的效果
import React, { Component,PureComponent } from 'react'
export default class Mount extends PureComponent {
//当类组件继承自PureComponent就相当于自动实现了,如下代码
/*
shouldComponentUpdate(newProps,newState){
return (newState.str !== this.state.str) || (newProps.cstr !== this.props.cstr)
}
*/
}
卸载阶段
卸载可以理解成到组件切换的时候,切换之前的组件会被卸载掉,在这个阶段只有一个生命周期函数
1、componentWillUnmount()
当组件卸载时触发
创建组件进行代码演示:
import React, { Component } from 'react'
export default class Unmount extends Component {
render() {
return (
<div>
</div>
)
}
componentWillUnmount(){
console.log("当组件卸载时")
}
}
然后,我们在index.js中通过setTimeout制作对root中渲染的组件做一个延迟执行
//......
import Unmount from './components/Unmount';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Unmount></Unmount>
);
//3秒之后渲染App组件,这个时候Unmount组件就会被替换掉从而触发卸载生命周期函数
setTimeout(() => {
root.render(
<App></App>
)
},3000)
我们来通过一个具体的小例子来看待卸载生命周期函数的作用
上面的index.js中的内容不动,依然模拟一个组件卸载的过程,然后把Unmount组件做一些修改
import React, { Component } from 'react'
export default class Unmount extends Component {
//到组件挂载之后给整个网页文档绑定一个点击事件
componentDidMount(){
document.addEventListener("click",this.dbClick)
}
//制作一个触发事件的方法
dbClick(){
console.log("我点击了这个网页")
}
render() {
return (
<div>
</div>
)
}
//到组件卸载的时候,把绑定在整个文档上的事件移除掉
componentWillUnmount(){
console.log("当组件卸载时")
document.removeEventListener("click",this.dbClick)
}
}
代码分析:
上面的代码主要情况就是,我们在Unmount组件挂载之后给全局绑定了一个点击事件,所以就算你组件卸载了,这个事件也不会受影响,所以我们在卸载的时候再次移除这个全局事件,从而保证虽然是一个全局事件,但是这个事件可以跟随Unmount组件的生命周期来走
React16+新增生命周期函数
在上面介绍的生命周期函数中,有部分是在新版本中被弃用的,取而代之的也有一些新增的生命周期函数
getDerivedStateFromProps函数
这是一个静态方法,所以在类组件中声明时记得带上static修饰符,它主要替代了挂载阶段中componentWillMount
和更新阶段中componentWillReceiveProps
这两个函数的位置,也就是说 getDerivedStateFromProps
在在初始化和后续更新都会被调用
该方法会接收两个参数
参数1:nextProps即将更新的props,父组件更新了传入到子组件中的数据时,更新后的数据会传入此参数
参数2:prevState 子组件内部的更新之前的状态(旧的状态)
**返回值:**该方法会返回一个对象来更新组件的内部状态state,如果返回null则不更新
注意事项:
使用该方法时,一定要设置state,就算是个空对象都行
在介绍该方法的作用之前我们需要先介绍扩展介绍一些知识点
派生状态
在介绍
getDerivedStateFromProps
方法的使用之前我们要先理解一种特殊的组件内部状态,派生状态,而介绍派生状态之前我们还要简单复习并扩展一下受控与非受控组件的内容
之前我们有介绍过受控组件和非受控组件,“受控”和“不受控制”主要在我们之前的介绍中主要指的是表单数据,但它还可以用于描述组件与组件之间的控制关系。作为props传递进子组件的数据可以被认为是受控的,所以我们认为该子组件的数据受控与父组件。只存在于内部状态state的数据可以被认为是不受控制的,因为这个数据不受父组件的控制。
举例
import React, { Component } from 'react'
export default class Mount extends Component { //父组件
constructor(props){
super(props)
this.state={
textVal:"zhangsan"
}
}
changeVal(){
this.setState({
textVal:"lisi"
})
}
render(){
return (
<div>
<h2>父组件:{this.state.textVal}</h2>
<button onClick={this.changeVal.bind(this)}>修改父组件state按钮</button>
<Son textVal={this.state.textVal} changeVal={this.changeVal.bind(this)}></Son>
</div>
)
}
}
class Son extends Component { //子组件
constructor(props){
super(props)
this.state = {
sonVal:this.props.textVal
}
}
changeVal(){
this.setState({
sonVal:"lisi"
})
}
render(){
return (
<div>
<h2>子组件state调用:{this.state.sonVal}</h2>
<h2>子组件props调用:{this.props.textVal}</h2>
<button onClick={this.changeVal.bind(this)}>修改子组件state按钮</button>
</div>
)
}
}
代码分析:
以上的情况我们可以认为,父组件向子组件传入了数据“zhangsan” ,但是在子组件中我们通过
sonVal:this.props.textVal
将该数据指派给了子组件自己的内部状态sonVal然后我们再来通过父组件Mount中修改状态的方法changeVal修改父组件的状态值时,子组件中sonVal的值并不会跟着一起改变,这种情况下虽然子组件的sonVal的值是通过父组件传入赋值得到的,但是它并不受控与父组件
而派生状态的使用就是为了让上面这种情况下的子组件中的sonVal依然还可以受控与父组件,也就是说子组件的内部状态值会根据父组件传入数据的改变而改变
注意点:
派生状态实现的功能与直接在子组件中通过props调用渲染父组件传入的数据所形成的结果十分相似,但是其原理是不同的
- props的实现方式始终都没有触及到子组件自身的内部状态,一致都使用的是父组件的数据
- 派生状态的实现其本质还是调用的组件自身的内部状态,子组件通过props将父组件传入的数据赋值给子组件的状态
如何实现派生状态
getDerivedStateFromProps这个方法的目的就只有一个,就是实现派生状态,使组件可以根据props的结果来更新其内部状态
注意:谨慎使用派生状态
举例:
import React, { Component } from 'react'
export default class Father extends Component {
state = {
fatherVal: "hello"
};
ChangeVal(e){
this.setState({ fatherVal: e.target.value });
};
render(){
return (
<div>
父组件:<input type="text" value={this.state.fatherVal} onChange={this.ChangeVal.bind(this)} />
<Son textVal={this.state.fatherVal}></Son>
</div>
)
}
}
class Son extends Component {
state = {
sonVal:this.props.textVal
}
render() {
return <h2>子组件:{this.state.sonVal}</h2>;
}
static getDerivedStateFromProps(nextProps){
return {sonVal: nextProps.textVal}
}
// componentWillReceiveProps(nextProps) {
// this.setState({ sonVal: nextProps.textVal });
// }
}
代码分析:
这里我们实现了派生状态,将father组件中的状态fatherVal传入Son组件,并指派给Son组件的内部状态sonVal,当父组件触发changeVal使父组件状态发生改变触发了组件更新进行重新渲染,而这个渲染过程也会波及到子组件使得子组件也会一并更新,而子组件的更新也必然会触发其生命周期函数
getDerivedStateFromProps
将父组件更新后的传入子组件中的数据从props中调出再次赋值给子组件的内部状态
使用派生状态遇到的常见bug
举例:对上面的例子做一点修改,现在我们把子组件中的派生状态sonVal渲染到一个表单中然后想让该表单与sonVal实现双向绑定
import React, { Component } from 'react'
export default class Father extends Component { //父组件内部不变
state = {
fatherVal: "hello",
num:1
};
changeVal(e){
this.setState({ fatherVal: e.target.value });
};
render(){
return (
<div>
父组件:<input type="text" value={this.state.fatherVal} onChange={this.changeVal.bind(this)} />
<hr />
<Son textVal={this.state.fatherVal}></Son>
</div>
)
}
}
class Son extends Component {
state = {
sonVal:this.props.textVal,
}
//制作双向绑定的方法
changeInput(e){
this.setState({sonVal:e.target.value})
}
render() {
return (
//把sonVal渲染把一个单表上,然后绑定onChange事件执行双向绑定
<div>
<h2>子组件:{this.state.sonVal}</h2>
<input type="text" value={this.state.sonVal} onChange={this.changeInput.bind(this)} />
</div>
)
}
static getDerivedStateFromProps(nextProps){
return {sonVal: nextProps.textVal}
}
// componentWillReceiveProps(nextProps) {
// this.setState({ sonVal: nextProps.textVal });
// }
}
代码分析:
上面这样写是有问题的,我们会发现这个input里面永远无法输入新的值进去
原因:
其实并不是写不进去数据,而是每当我们输入一个数据的时候就会触发组件的更新,而组件一更新就会重新渲染,而一旦重新渲染sonVal的值就又重新被指派成了父组件传入的textVal的值,在input的value上面渲染的数据就又会变成父组件指派的数据,周而复始,只要一直持续输入新数据就一直这样,导致在input中新输入的数据一直被覆盖掉,无法将新数据保留下来
解决方案:完全受控组件
直接全程props,从子组件中彻底删除状态,子组件直接使用props不再去组件内部接收父组件的指派状态
import React, { Component } from 'react' export default class Father extends Component { state = { fatherVal: "hello", num:1 }; changeVal(e){ this.setState({ fatherVal: e.target.value }); }; render(){ return ( <div> 父组件:<input type="text" value={this.state.fatherVal} onChange={this.changeVal.bind(this)} /> <hr /> <Son textVal={this.state.fatherVal} changeVal={this.changeVal.bind(this)}></Son> </div> ) } } class Son extends Component { state = { sonVal:this.props.textVal, } render() { return ( <div> <h2>子组件:{this.state.sonVal}</h2> <input type="text" value={this.props.textVal} onChange={this.props.changeVal.bind(this)} /> </div> ) } }
getSnapshotBeforeUpdate函数
这是一个新增的更新阶段的生命周期函数,其在render后触发,其接收两个参数,还有一个返回值
参数1:更新之前的props
参数2:更新之前的state
返回值:该返回值会作为componentDidUpdate生命周期函数的第三个参数传入,一般我们会返回更新之前的一些页面状态,从而进行页面的状态保持
注意:新旧生命周期函数不能同时使用,不然会报错
举例:
现在有一个移动端页面,我现在希望当组件更新之后,调整当前滚动的位置
import React, { Component } from 'react'
import './snapshot.css'
export default class SnapShot extends Component {
state = {
num:1
}
//当组件加载时设置一个定时器,4秒之后修改状态从而能够触发组件更新
componentDidMount(){
this.clearTime = setTimeout(() => {
this.setState({
num:2
})
},4000)
console.log(this.scrollSize)
}
//一般情况下,我们都会把组件内设置的定时器,在组件卸载时清除掉
componentWillUnmount(){
clearTimeout(this.clearTime)
}
render() {
return (
//通过ref获取到滚动容器的DOM
<div className='wrap' ref={ref => this.wrap = ref}>
<div className="box1">1111</div>
<div className="box2">{this.state.num}</div>
</div>
)
}
//当开始更新时,将更新之前的wrap元素的scrollTop返回出去
getSnapshotBeforeUpdate(prevProps,prevState){
return this.wrap.scrollTop
}
//在更新之后,通过componentDidUpdate的第三个参数调出wrap元素更新之前的scrollTop的值减去300,再赋值给更新之后的wrap
componentDidUpdate(prevProps,prevState,prevScrollTop){
this.wrap.scrollTop = prevScrollTop - 300
}
}
SnapShot.css
*{
padding:0;
margin:0;
}
.wrap{
width:100vw;
height:100vh;
overflow: auto;
}
.box1{
height:1000px;
background:#f00;
}
.box2{
height:1000px;
background:#0f0;
}
代码分析:
上面的例子中,我们的SnapShot组件应该会在加载好4秒之后自动修改内部状态,从而触发更新周期,然后在当前滚动的位置向上自动滚动 300px 的位置,这里主要依靠的就是
getSnapshotBeforeUpdate
将更新之前的scrollTop的值返回出去,然后更新之后的生命周期函数componentDidUpdate
可以调用到,并将更新之前的滚动位置信息减去300再赋值给更新之后的scrollTop
特殊生命周期函数 – React异常捕获
从React16开始引入了一个新的概念叫做错误边界
错误边界可以理解成是一个React组件,其应用场景与概念比较类似于我们之前在vue中讲过的异步组件,只不过它并不是一个Promise对象,这种组件可以捕获并打印发生在其子组件树任何位置的原生JavaScript错误,并且渲染出错误情况下的UI界面,而不是渲染错误的子组件树
目前只有类组件可以成为错误边界组件,要实现错误边界组件需要使用以下两个函数
getDerivedStateFromError(error)
componentDidCatch(error,info)
以上两个函数任意一个或者两个都用都可以构成一个错误边界组件,两个函数的功能类似
getDerivedStateFromError(error)
该方法是一个静态方法,接收一个参数,并返回一个对象
参数:错误信息
返回值:返回一个匿名对象,这个对象直接指向当前组件的state
举例:
import React, { Component } from 'react'
export default class ErrorComp extends Component {
constructor(props){
super(props)
this.state = {
error:false //error用来表示当前错误边界组件是否有捕获到错误
}
}
static getDerivedStateFromError(error){
console.log(error) //打印错误
return {
error:true //这里error是ErrorCpmp组件的内部状态error
}
}
render() {
if(this.state.error){
return (
<p>我是错误位置:{this.state.text}</p>
)
}else{
return (
<Child></Child>
)
}
}
}
class Child extends Component {
render(){
throw new Error("我是一个错误") //在子组件中认为抛出一个错误来触发getDerivedStateFromError
return (
<div>渲染Child组件</div>
)
}
}
代码分析:
上面的代码中,我们创建了一个ErrorComp组件作为错误边界组件,在内部调用 getDerivedStateFromError ,同时创建一个子组件Child并在其渲染的时候手动抛出一个错误,当组件渲染时,ErrorComp接收到子组件Child抛出的错误触发 getDerivedStateFromError 通过该方法的返回值修改error状态的值为true,在ErrorComp组件render时,根据error状态的值做一个判断来决定渲染哪个子组件
componentDidCatch(error,info)
该方法的作用其实与上面的方法基本上一致,就是执行逻辑不太一样,接收两个参数,没有返回值
参数1:错误信息
参数2:自动注入一个对象,对象内部有一个componentStack属性记录当前错误的路径
举例:
import React, { Component } from 'react'
export default class ErrorComp extends Component {
constructor(props){
super(props)
this.state = {
error:false,
text:''
}
}
componentDidCatch(error,info){
console.log(error,info)
this.setState({ //通过setState修改内部状态(与上面的return其实是一样的)
error:error,
text:info.componentStack //把错误信息路径赋值给text
})
}
render() {
return (
<div>
{
//即然是判断,我们也可以采用三元运算完成
this.state.error ? <p>{this.state.text}</p> : <Child></Child>
}
</div>
)
}
}
class Child extends Component {
render(){
throw new Error("我是一个错误")
return (
<div>渲染Child组件</div>
)
}
}
代码分析:
上面的例子其实执行的效果与getDerivedStateFromError方法的执行效果一样,只不过可以多接收一个参数记录错误位置,一般我们可以把这个错误信息作为错误日志上传给服务器,同时因为没有return的方式修改错误状态error的值,所以我们直接使用setState来修改
以上两个方法如果要一起用的话,我们可以用 getDerivedStateFromError 来修改错误状态值,用componentDidCatch 来调用一个上传接口上传把错误信息作为上传错误日志
注意事项:
- 错误边界组件只能捕获自己的子组件中的错误,所以我们一般会专门制作一个需要在某个子组件错误时进行渲染的备用组件,类似与在vue中如果异步组件的Promise返回的是一个错误状态就显示注册到错误状态下的组件
- 在开发环境下就算渲染出来了备用组件,依然还是会弹出React自己的报错页面,生产环境下不会,所以说这些错误边界组件主要是为生产环境使用的
无法捕获异常的情况
下面这些情况下错误边界无法捕获到异常:
- 事件处理
- 异步代码
- 服务端渲染
- 自身抛出来的错误
对于错误边界无法捕获的异常,可以使用js
的try...catch...
语法
举例:我们在当前组件自身内部通过点击事件触发一个错误抛出来实现上面两个异常捕获方法所实现的功能
import React, { Component } from 'react'
export default class ErrorComp extends Component {
constructor(props){
super(props)
this.state = {
error:null
}
}
handleClick() {
try {
throw new Error("我是一个错误") //在try中手动制作一个错误抛出让catch被执行
} catch (error) {
this.setState({ error });
}
}
render() {
return (
<div>
{
this.state.error ? <p>{'我是错误的'}</p> : <p>{'我是正确的'}</p>
}
<button onClick={this.handleClick.bind(this)}>按钮</button>
</div>
)
}
}
代码分析:
我们通过点击事件触发一个try…catch语句的执行,并且在try中故意抛出一个错误,从而修改error的状态值,来控制当前组件自己的渲染结果
总结:
老生命周期函数:
挂载阶段:
-
constructor()
初始化整个组件的state 以及调用super(props),该方法只会执行一次
-
componentWillMount()
挂载之前(已经弃用,目前React18.2中依然还是可以使用,如果使用会报警告)
-
render()
只返回需要渲染页面结构,最好不要包含其它的业务逻辑
-
componentDidMount()
挂载之后,可以获取到DOM节点并操作,服务器请求,启用事件监听,调用 setState()等都可以在此函数中
更新阶段情况一:state更新时
-
shouldComponentUpdate(nextProps,nextState)
有两个参数
nextProps
和nextState
,表示新的属性和变化之后的state
,返回一个布尔值,true
表示会触发重新渲染,false
表示不会触发重新渲染,默认返回true
,我们通常利用此生命周期来优化React程序性能 -
componentWillUpdate()
准备开始更新(已经弃用,目前React18.2中依然还是可以使用,如果使用会报警告)
-
render()
重新渲染页面结构(与挂载阶段共享同一个函数)
-
componentDidUpdate()
在组件完成更新后立即调用,在初始化时不会被调用
更新阶段情况二:props更新时
-
componentWillReceiveProps(nextProps)
外部传入的数据发生变化时触发(父组件修改了一个自己组件内部的数据,而这个被修改的数据时有传递给子组件的)
-
shouldComponentUpdate(nextProps,nextState)
有两个参数
nextProps
和nextState
,表示新的属性和变化之后的state
,返回一个布尔值,true
表示会触发重新渲染,false
表示不会触发重新渲染,默认返回true
,我们通常利用此生命周期来优化React程序性能 -
componentWillUpdate()
准备开始更新(已经弃用,目前React18.2中依然还是可以使用,如果使用会报警告)
-
render()
重新渲染页面结构(与挂载阶段共享同一个函数)
-
componentDidUpdate()
在组件完成更新后立即调用,在初始化时不会被调用
卸载阶段:
1、componentWillUnmount()
当组件卸载时触发,我们一般在这个函数里去清除一些定时器,取消网络请求,清理无效的DOM元素等垃圾清理工作。
以上已被弃用的声明周期函数
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
但是在当前React18的版本中依然还是可以使用,但是回报警告,要求加上前缀UNSAFE_
新生命周期函数
挂载阶段:
-
constructor()
初始化整个组件的state 以及调用super(props),该方法只会执行一次
-
getDerivedStateFromProps(nextProps,prevState)
这是个静态方法,使用时记得添加static修饰符,在初始挂载及后续更新时都会被调用,接收两个参数分别是新传入的props和修改之后的state,它返回一个对象来更新 state,如果返回 null 则不更新任何内容。
-
render
只返回需要渲染页面结构,最好不要包含其它的业务逻辑
-
componentDidMount()
组件装载之后调用,可以获取到DOM节点并操作,服务器请求,启用事件监听,调用 setState()等都可以在此函数中
更新阶段
-
getDerivedStateFromProps()
此方法在更新和挂载阶段都会调用
-
shouldComponentUpdate(nextProps,nextState)
该方法有两个参数nextProps和nextState,表示新的属性和变化之后的state,返回一个布尔值,true表示会触发重新渲染,false表示不会触发重新渲染,默认返回true,我们通常利用此生命周期来优化React程序性能
-
render()
重新渲染页面结构(与挂载阶段共享同一个函数)
-
getSnapshotBeforeUpdate(prevProps,prevState)
这个方法在render之后,componentDidUpdate之前调用,有两个参数prevProps和prevState,表示之前的属性和之前的state,这个函数有一个返回值,会作为第三个参数传给componentDidUpdate,如果你不想要返回值,可以返回null,此生命周期需要与componentDidUpdate搭配使用
-
componentDidUpdate(prevProps,prevState,snapshot)
该方法在getSnapshotBeforeUpdate方法之后被调用,有三个参数prevProps,prevState,snapshot,表示之前的props,之前的state,和snapshot。第三个参数是getSnapshotBeforeUpdate返回的,如果触发某些回调函数时需要用到 DOM 元素的状态,则将对比或计算的过程迁移至getSnapshotBeforeUpdate,然后在 componentDidUpdate中统一触发回调或更新状态。
卸载阶段
-
componentWillUnmount()文章来源:https://www.toymoban.com/news/detail-833280.html
当组件被卸载或者销毁了就会调用,我们可以在这个函数里去清除一些定时器,取消网络请求,清理无效的DOM元素等垃圾清理工作。文章来源地址https://www.toymoban.com/news/detail-833280.html
到了这里,关于【学习前端第七十四课】React生命周期的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!