React之生命周期
旧版本,函数组件是没有生命周期的。新版本中通过useEffect触发函数的生命周期
一、基于类组件的生命周期
React的组件生命周期分为
挂载阶段
、更新阶段
和销毁阶段
。因为React的state不具有Vue的响应式,所以并没有create阶段
1、挂载阶段: 【组件被创建并插入到DOM中的过程】
-
constructor:组件被创建时调用,用于初始化状态和绑定方法。
-
static getDerivedStateFromProps:在组件被创建和重新渲染之前调用,用于根据props更新状态。
-
【已废弃】componentWillMount: 组件即将挂载,已经被废弃,向下兼容请使用【UNSAFE_componentWillMount】
- 执行1次
- 特点:可以访问state、不能操作DOM
-
render: 对组件视图结构进行编译【必须定义的一个钩子】
- 先默认执行1次,后去每次setState都会执行一次
- 特点:可以访问state、不能操作DOM
-
componentDidMount: 组件挂载完成
- 执行一次
- 特点: 可以访问state,也能操作DOM
- 应用场景:发送请求;初始化第三方插件
import { Component } from 'react'
export default class Home extends Component {
state = {
title: 'home'
}
clickUpdate = () => {
this.setState({
title: '主页'
})
}
UNSAFE_componentWillMount(): void {
console.log(this.state.title) // 'title'
console.log(document.querySelector('.home_box')) // null
}
render() {
console.log(this.state.title, 'render') // 'title'
console.log(document.querySelector('.home_box'), 'render') // null
return (
<>
<div className="home_box">{this.state.title}</div>
<button onClick={this.clickUpdate}>获取DOM</button>
</>
)
}
componentDidMount(): void {
console.log(this.state.title) // 'title'
console.log(document.querySelector('.home_box')) // <div class="home_box">home</div>
}
}
2、更新阶段【组件的props或state发生变化时的过程】
- static getDerivedStateFromProps(props,state):在组件被重新渲染之前调用,用于根据props更新状态。[替代了componentWillReceiveProps]
- shouldComponentUpdate: 是否要执行组件更新。如果显示的声明了该钩子,则必须返回一个布尔值,true:执行组件更新;false:不执行组件更新。如果没有显示的声明该钩子,则正常执行组件更新
- 应用场景: 手动进行性能优化
- 触发条件:state状态数据发生改变
- 系统参数:nextProps: 更新后的props; nextState:更新后的state
- 特点: 还没有完成数据和视图的更新
- 【已废弃】componentWillUpdate: 即将执行组件更新: *已经被废弃,向下兼容请使用【UNSAFE_componentWillUpdate】
- 触发条件:state状态数据发生改变
- 系统参数:nextProps: 更新后的props; nextState:更新后的state
- 特点: 还没有完成数据和视图的更新
- render: 重新执行视图编译
- 触发条件:state状态数据发生改变
- 特点: 完成数据更新, 但是还没有完成视图更新
- getSnapshotBeforeUpdate:在组件被重新渲染之前调用,用于获取更新前的DOM状态。[替换componetnWillUpdate]
- componentDidUpdate:组件更新完成
- 触发条件:state状态数据发生改变
- 特点:已经完成数据和视图的更新
- 系统参数:prevProps: 更新之前的props; prevState:更新之前的state【此时this.state是更新后的state】
- 场景: 进行DOM操作、网络请求等副作用操作。
- componentWillReceiveProps:组件接收的外部props属性数据发生改变的时候执行: *已经被废弃,向下兼容请使用【UNSAFE_componentWillReceiveProps】
- 触发条件:props数据发生改变
- 特点:已经完成数据和视图的更新
import { Component } from 'react'
export default class Home extends Component {
state = {
title: 'home'
}
clickUpdate = () => {
this.setState({
title: '主页'
})
}
UNSAFE_componentWillMount(): void {
console.log(this.state.title) // 'title'
console.log(document.querySelector('.home_box')) // null
}
render() {
console.log(this.state.title, 'render') // 'title'
console.log(document.querySelector('.home_box'), 'render') // null
return (
<>
<div className="home_box">{this.state.title}</div>
<button onClick={this.clickUpdate}>获取DOM</button>
</>
)
}
componentDidMount(): void {
console.log(this.state.title) // 'title'
console.log(document.querySelector('.home_box')) // <div class="home_box">home</div>
}
// 更新阶段
shouldComponentUpdate(
nextProps: Readonly<{}>,
nextState: Readonly<{ title: string }>,
nextContext: any
): boolean {
// nextProps: {}, nextState: 更新后的state, nextContext: {}
console.log(nextProps, nextState, nextContext)
if (this.state.title === nextState.title) {
// 如果数据没有发生变化 则不执行组件更新, 即是不执行render()钩子
return false
}
return true
}
// componentWillUpdate: 严格模式下eslint, 会报错
// UNSAFE_componentWillUpdate: 这个在高版本React也废弃了, 但是在ESLint模式下也可以使用[向下兼容]
UNSAFE_componentWillUpdate(
nextProps: Readonly<{}>,
nextState: Readonly<{}>,
nextContext: any
): void {
console.log(nextProps, nextState, nextContext, 'UNSAFE_componentWillUpdate')
}
componentDidUpdate(
prevProps: Readonly<{}>,
prevState: Readonly<{}>,
snapshot?: any
): void {
console.log(prevProps, prevState, snapshot, 'componentDidUpdate')
}
UNSAFE_componentWillReceiveProps(
nextProps: Readonly<{}>,
nextContext: any
): void {
console.log(nextProps, nextContext, 'UNSAFE_componentWillReceiveProps')
}
}
3、销毁阶段: 【组件从DOM中移除的过程】
- componentWillUnmount:组件被移除前调用,可以进行清理操作,如取消定时器、取消订阅等。
4、补充
除了上述方法,还有一些其他的生命周期方法,如错误处理相关的方法(componentDidCatch、getDerivedStateFromError)和静态方法(getDerivedStateFromProps、getDerivedStateFromError)
需要注意的是,React 16.3之后,一些生命周期方法已经被废弃或改名,如componentWillMount、componentWillUpdate、componentWillReceiveProps等,建议使用新的方法来替代
- 旧版生命周期图
- 新版生命周期图
二、函数组件的生命周期
React 18引入了一些新的生命周期方法,以便更好地管理组件的生命周期事件
useEffect
: 【异步执行的,不会阻塞渲染】用于在组件挂载、更新或卸载时执行副作用操作。它可以替代旧的生命周期方法componentDidMount、componentDidUpdate和componentWillUnmount
import { useState, useEffect } from 'react'
const Home = () => {
const [count, setCount] = useState(0)
const add = () => {
setCount(count + 1)
}
// 接收一个参数时:组件加载完成和组件状态更新时 执行
useEffect(function () {
console.log('zhw')
})
// 如果只希望函数挂载完后,执行一次,组件状态变化不再执行,可以传空数组
useEffect(function () {
console.log('zhw')
}, [])
// 接收参数二时,参数二是一个数组,用于指定依赖数据,用于当依赖数据变化时,执行一次回调
// 1、组件挂载完后执行一次;2、仅仅当count的状态更新,执行一次
useEffect(function () {
console.log('zhw')
}, [count])
// 组件卸载之前按
// 1、
useEffect(function () {
return function () {
// 该子函数会在组件 即将卸载 和 状态更新 的时候,自动执行
console.log('组件卸载了')
}
})
// 2、
useEffect(function () {
return function () {
// 该子函数会在组件 仅仅在 即将卸载 的时候,自动执行
console.log('组件卸载了')
}
}, [])
return (
<>
<span>{count}</span>
<button onClick={add}>+</button>
</>
)
}
export default Home
useLayoutEffect
: 在DOM更新之后同步执行[阻塞渲染],以确保在浏览器绘制之前执行副作用操作。
适用于需要在浏览器执行绘制和布局之前立即执行的副作用操作。
import { useLayoutEffect, useState } from 'react'
const Home = () => {
const [count, setCount] = useState(0)
const add = () => {
setCount(count + 1)
}
useLayoutEffect(
function () {
console.log('useLayoutEffect')
},
[count]
)
return (
<>
<span>{count}</span>
<button onClick={add}>+</button>
</>
)
}
export default Home