旧版 React 生命周期方法执行顺序(React 16.3 之前)
挂载阶段(Mounting)
当组件实例被创建并插入 DOM 中时,执行顺序如下:
- constructor: 组件的构造函数,在创建组件实例时最先调用,通常用于初始化
state
和绑定事件处理方法。 - componentWillMount: 在组件即将被挂载到 DOM 之前调用,此方法在服务器端渲染时也会调用。不过从 React 16 开始,它被标记为不安全的,不建议使用。
- render: 返回组件的 JSX 结构,用于创建虚拟 DOM。这是一个纯函数,不应该有任何副作用。
- componentDidMount: 组件已经被挂载到 DOM 后调用,适合进行网络请求、订阅事件、操作 DOM 等操作。
更新阶段(Updating)
当组件的 props
或 state
发生变化时,会触发更新阶段,执行顺序如下:
- componentWillReceiveProps: 在组件接收到新的
props
时调用(初始挂载时不会调用),可以在这个方法中根据新的props
更新state
。该方法从 React 16 开始被标记为不安全,不建议使用。 - shouldComponentUpdate: 返回一个布尔值,决定组件是否需要重新渲染。默认返回 true,通过合理实现这个方法可以避免不必要的渲染,提高性能。
- componentWillUpdate: 在组件即将重新渲染之前调用,在
shouldComponentUpdate
返回 true 之后执行。同样从 React 16 开始,它被标记为不安全,不建议使用。 - render: 重新渲染组件,生成新的虚拟 DOM。
- componentDidUpdate: 组件已经重新渲染完成后调用,可用于操作更新后的 DOM 或者进行额外的网络请求。
卸载阶段(Unmounting)
当组件从 DOM 中移除时,执行以下方法:
- componentWillUnmount: 在组件即将被卸载之前调用,通常用于清理定时器、取消网络请求、取消订阅等操作,防止内存泄漏。
新版 React 生命周期方法执行顺序(React 16.3 及之后)
挂载阶段(Mounting)
- constructor: 同旧版,用于初始化 state 和绑定事件处理方法。
static getDerivedStateFromProps: 这是一个静态方法,在组件实例化之后和接收新 props 时被调用。它应该返回一个对象来更新 state,或者返回 null 表示不更新任何内容。该方法替代了componentWillReceiveProps
和componentWillMount
的部分功能。 - render: 返回组件的 JSX 结构。
- componentDidMount: 组件挂载完成后调用,用途和旧版相同。
更新阶段(Updating)
- static getDerivedStateFromProps: 在组件接收到新 props 时再次调用,用于根据新
props
更新state
。 - shouldComponentUpdate: 同旧版,决定组件是否需要重新渲染。
- render: 重新渲染组件。
- getSnapshotBeforeUpdate: 在最新的渲染输出提交到 DOM 前调用,它可以返回一个值作为
componentDidUpdate
的第三个参数,常用于保存滚动位置等信息。 - componentDidUpdate: 组件更新完成后调用,可获取
getSnapshotBeforeUpdate
返回的值。
卸载阶段(Unmounting)
- componentWillUnmount: 和旧版一样,用于在组件卸载前进行清理操作。
错误处理阶段(React 16 引入)
当渲染过程、生命周期方法或子组件的构造函数中抛出错误时,会触发以下方法:
- componentDidCatch: 在子组件抛出错误后调用,可用于捕获错误并进行错误处理,显示错误信息等。
React 17 和 React 18 的变更
-
React 17 中,
UNSAFE_componentWillMount
,UNSAFE_componentWillReceiveProps
, 和UNSAFE_componentWillUpdate
被标记为不推荐使用,并被替换为新的生命周期钩子。 -
React 18 中,彻底移除了所有不推荐使用的生命周期方法,并鼓励使用函数组件和 Hook,如
useState
,useEffect
,useContext
等。
使用函数组件和 Hooks
对于函数组件,React 提供了 Hooks,如 useState, useEffect, useContext 等,这些 Hooks 可以让你在不编写 class 的情况下使用 state 和其他 React 特性。例如,useEffect 可以看作是 componentDidMount, componentDidUpdate, 和 componentWillUnmount 的组合。这使得函数组件能够更灵活地处理副作用和更新逻辑。
生命周期示例代码及输出顺序验证
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
console.log('Constructor called');
}
static getDerivedStateFromProps(props, state) {
console.log('getDerivedStateFromProps called');
return null;
}
componentDidMount() {
console.log('componentDidMount called');
}
shouldComponentUpdate(nextProps, nextState) {
console.log('shouldComponentUpdate called');
return nextState.count!== this.state.count;
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log('getSnapshotBeforeUpdate called');
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('componentDidUpdate called');
}
componentWillUnmount() {
console.log('componentWillUnmount called');
}
handleClick = () => {
this.setState(prevState => ({
count: prevState.count + 1
}));
};
render() {
console.log('render called');
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.handleClick}>Increment</button>
</div>
);
}
}
export default MyComponent;
在上述代码中,当组件挂载时,控制台输出顺序为:
Constructor called
getDerivedStateFromProps called
render called
componentDidMount called
当点击按钮更新 state 时,控制台输出顺序为:
getDerivedStateFromProps called
shouldComponentUpdate called
render called
getSnapshotBeforeUpdate called
componentDidUpdate called
当组件卸载时,控制台输出:
componentWillUnmount called