一看就懂的React生命周期
一看就懂的React生命周期
简介
React生命周期是一个不断演进的概念,特别是在16.3版本后。这一演进包括了弃用某些生命周期钩子并引入全新的钩子函数。在本文中,我们将会从以下四点开始讲解:
- 新旧生命周期对比--图解
- 旧的生命周期
- 新的生命周期
- 总结
新旧生命周期对比
什么是生命周期:react实例从创建到销毁的过程,就是生命周期。一般来说,生命周期分为创建、挂载、更新、卸载四个阶段。 从旧生命周期入手,由旧到新,把旧的看懂了,理解新的也就事半功倍,废话不多说,看下图(图片均来自网络)
旧的:
新的:
通过对比不难发现,新的生命周期中不再使用以下三个钩子函数componentWillMount()
、componentWillReceiveProps(nextProps)
、componentWillUpdate(nextProps, nextState)
为何不再使用
1、componentWillMount()
不推荐使用的原因主要是它的执行时机可能会导致一些问题。具体来说,如果你在componentWillMount()
中触发了异步操作,可能会导致在组件卸载前仍然执行未完成的操作,这可能会引发潜在的错误。
2、componentWillReceiveProps(nextProps)
这个生命周期方法被不推荐使用,因为它容易导致状态不一致的问题。在这个方法中,你可以在组件接收新的props之前执行某些操作,但它不适合进行依赖于props的状态更新。
3、componentWillReceiveProps(nextProps)
与componentWillReceiveProps()
类似,这个方法也容易导致状态不一致。
需要详细了解的小伙伴可以阅读以下文章:为什么废弃react生命周期函数?
详解旧的生命周期
一、三个阶段
1. 初始化阶段 : 由ReactDOM.render()触发---初次渲染,会调用以下钩子函数
1. constructor()
2. componentWillMount()
3. render()
4. componentDidMount()
2. 更新阶段 : 由组件内部this.setSate()或父组件重新render触发,会调用以下钩子函数
1. shouldComponentUpdate()
2. componentWillUpdate()
3. render()
4. componentDidUpdate()
3. 卸载组件 : 由ReactDOM.unmountComponentAtNode()触发
1. componentWillUnmount()
二、详解各个钩子函数
因
componentWillMount()
、componentWillReceiveProps()
、componentWillUpdate()
三个钩子函数不再推荐使用,故不再详细解释说明。
-
constructor()
constructor()
:构造函数,通常用于初始化组件的状态和绑定方法。
constructor(props) {
super(props);
this.state = { count: 0 };
this.handleClick = this.handleClick.bind(this);
}
在函数体中,需要先写super(props);
,想深入探究的小伙伴可以点我
-
render()
render()
:是用来返回组件的UI结构,它是一个纯函数,其中不应该包含任何副作用或改变状态的操作。
import React,{Component} from 'react'
export default class Hello extends Component{
render(){
return <h2>Hello,React!</h2>
}
}
-
componentDidMount()
componentDidMount()
:这个函数是在组件挂载到DOM后执行的,可以在这里获取数据、进行一些异步请求或DOM操作。
componentDidMount() {
// 发起API请求或其他初始化操作
fetchData().then(data => {
this.setState({ data
});
});
}
-
shouldComponentUpdate()
shouldComponentUpdate(nextProps, nextState)
:这个函数是用来判断组件是否需要重新渲染,返回一个布尔值,可以优化性能。
shouldComponentUpdate(nextProps, nextState) {
// 这里假设props有一个min属性,表示计数器的最小值
if (nextProps.min !== undefined && nextState.count < nextProps.min) {
return false;
}
return true;
}
细心观察旧的生命周期图的小伙伴注意到了此钩子函数旁边有个setState()
,在组件中,每当调用this.setState()
方法时,就会触发shouldComponentUpdate()
,如果函数返回true
就更新状态,否则不更新。
forceUpdate():强制更新
当调用forceUpdate()
时,会直接来到componentWillUpdate()
钩子函数。
何时使用: 只想更新一下组件,但是不想对状态作出任何的更改,就可以调用,通过this.forceUpdate()
直接调用
-
componentDidUpdate()
componentDidUpdate(prevProps, prevState)
:组件更新后触发,用于处理更新后的操作。
componentDidUpdate(prevProps, prevState) {
console.log('组件已更新完毕');
if (prevProps.id !== this.props.id) {
// 处理props变化的逻辑
console.log('props.id变了');
}
}
-
componentWillUnmount()
componentWillUnmount()
:这个函数是在组件卸载前执行的,可以在这里进行一些清理工作,比如取消订阅、清除定时器、取消异步请求或移除事件监听器等。
详解新生命周期
一、三个阶段
1. 初始化阶段 : 由ReactDOM.render()触发---初次渲染
1. constructor()
2. getDerivedStateFromProps
3. render()
4. componentDidMount()
2. 更新阶段 : 由组件内部this.setSate()或父组件重新render触发
1. getDerivedStateFromProps
2. shouldComponentUpdate()
3. render()
4. getSnapshotBeforeUpdate
5. componentDidUpdate()
3. 卸载组件 : 由ReactDOM.unmountComponentAtNode()触发
1. componentWillUnmount()
二、详解新增的钩子函数
部分钩子函数用法与旧生命周期中的用法相似,不再重复说明。
变化:
-
componentDidUpdate()
componentDidUpdate(prevProps,prevState,snapshot)
:它在组件更新(即render()
方法执行后)后被调用。它接收三个参数:prevProps
、prevState
、snapshot
。与旧的钩子函数相比,多了一个参数snapshot
。
componentDidUpdate(prevProps, prevState, snapshot) {
if (this.props.data !== prevProps.data) {
console.log('this.props中的数据变了');
}
// 使用getSnapshotBeforeUpdate()的返回值
if (snapshot !== null) {
console.log('Snapshot from getSnapshotBeforeUpdate:', snapshot);
}
}
说明:如果
getSnapshotBeforeUpdate()
有返回值,它会成为componentDidUpdate()
的第三个参数,你可以在这里使用它。
新增:
-
getDerivedStateFromProps()
getDerivedStateFromProps(nextProps,prevState)
:是一个静态方法,用于在组件接收新的props时计算并返回新的state。这个方法在React 16.3
版本之后引入,用来替代不推荐使用的componentWillReceiveProps()
方法。
class MyComponent extends React.Component {
static getDerivedStateFromProps(nextProps, prevState) {
// 根据 nextProps 和 prevState 计算并返回新的 state
if (nextProps.value !== prevState.value) {
return { value: nextProps.value };
}
return null; // 如果不需要更新 state,返回 null
}
constructor(props) {
super(props);
this.state = {
value: props.value,
};
}
render() {
return <div>{this.state.value}</div>;
}
}
以下是getDerivedStateFromProps()
的主要特点和使用方式:
- 静态方法:
getDerivedStateFromProps()
是一个静态方法,因此不能访问实例的this,它只接收两个参数:nextProps
和prevState
。- 计算新的state:通常,你可以在这个方法内部根据
nextProps
和prevState
来计算并返回新的state。这个新的state将在组件更新时应用。- 不触发副作用:与
componentDidUpdate()
不同,getDerivedStateFromProps()
不应执行副作用,如发起网络请求。它只用于计算state。- 适用于控制组件内部状态:
getDerivedStateFromProps()
主要用于控制组件内部的状态,以确保它与外部传入的props保持同步。
-
getSnapshotBeforeUpdate()
getSnapshotBeforeUpdate(nextProps,prevState)
:它在组件更新(即将应用新props或state并重新渲染)之前触发。它允许你捕获组件更新前的一些信息,并在组件更新后使用这些信息。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
getSnapshotBeforeUpdate(prevProps, prevState) {
// 捕获组件更新前的滚动位置
if (prevProps.items.length < this.props.items.length) {
const scrollHeight = this.myRef.current.scrollHeight;
const scrollTop = this.myRef.current.scrollTop;
return { scrollHeight, scrollTop };
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// 使用snapshot来恢复滚动位置
if (snapshot !== null) {
this.myRef.current.scrollTop = snapshot.scrollTop + (this.myRef.current.scrollHeight - snapshot.scrollHeight);
}
}
render() {
// 使用ref来获取DOM元素的引用
return <div ref={this.myRef}>{/* 组件内容 */}</div>;
}
}
示例中,getSnapshotBeforeUpdate()
用于捕获滚动位置,然后在componentDidUpdate()
中使用snapshot来恢复滚动位置,以确保用户在滚动列表时不会在更新后失去滚动位置。
以下是关于getSnapshotBeforeUpdate()
的主要特点和用法:
- 触发时机:
getSnapshotBeforeUpdate()
在render()
方法被调用后、组件DOM更新前触发,通常用于在更新前捕获一些DOM信息。- 接收两个参数:这个生命周期方法接收两个参数:
prevProps
、prevState
。你可以使用这些参数来比较前后的props和state。- 返回值:
getSnapshotBeforeUpdate()
方法应该返回一个值(通常是一个对象),它将成为componentDidUpdate()
方法的第三个参数。这个返回值通常用于保存一些DOM相关的信息,比如滚动位置。- 通常和componentDidUpdate()一起使用:
getSnapshotBeforeUpdate()
结合componentDidUpdate(prevProps, prevState, snapshot)
使用,snapshot参数是getSnapshotBeforeUpdate()
的返回值。你可以在componentDidUpdate()
中使用snapshot来执行DOM操作或其他一些操作。
总结
通过本篇文章的学习,相信小伙伴们已经全面了解了React生命周期的执行流程以及各个生命周期函数的作用。
通过正确地使用这些生命周期函数,我们可以更好地控制组件的行为,提高应用性能,同时保持代码的可读性和可维护性。希望本文对你理解React生命周期有所帮助,也能够在实际项目中灵活运用,构建出更好的用户体验。
如果你有任何问题或疑惑,也请随时在评论区提问,我将尽力解答。感谢阅读!
转载自:https://juejin.cn/post/7285540804734468150