React 生命周期机制
React组件生命周期一直是开发者关注的重点之一。随着 React 16.8 引入 Hooks,以及 React 18 带来的并发特性,生命周期的概念逐渐从「固定钩子」演进为「更具声明性和粒度控制的执行机制」。
本篇文章将从以下几个方面带你全面了解 React 的生命周期:
- 生命周期概览:Class vs Function
- Class 组件生命周期详解
- Function 组件生命周期 & Hooks
- React 18 并发渲染对生命周期的影响
- 实践场景和注意事项
一、生命周期概览:Class vs Function
阶段 | Class 组件(传统) | Function 组件(Hooks) |
---|---|---|
初始化 | constructor → render → componentDidMount | useState , useEffect(..., []) |
更新 | shouldComponentUpdate → render → componentDidUpdate | useEffect(...) (有依赖) |
卸载 | componentWillUnmount | useEffect 的清理函数 |
错误捕获 | componentDidCatch , getDerivedStateFromError | useErrorBoundary (三方库)或错误边界组件 |
二、Class 组件生命周期详解(不推荐新项目使用)
class Example extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
console.log("constructor");
}
static getDerivedStateFromProps(nextProps, prevState) {
console.log("getDerivedStateFromProps");
return null;
}
componentDidMount() {
console.log("componentDidMount");
}
shouldComponentUpdate(nextProps, nextState) {
console.log("shouldComponentUpdate");
return true;
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("getSnapshotBeforeUpdate");
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("componentDidUpdate");
}
componentWillUnmount() {
console.log("componentWillUnmount");
}
render() {
console.log("render");
return <div>{this.state.count}</div>;
}
}
生命周期阶段说明:
-
初始化阶段
constructor
:设置初始状态。getDerivedStateFromProps
:由 props 派生 state(极少用)。render
:渲染 UI。componentDidMount
:挂载完成,可进行 DOM 操作、请求数据。
-
更新阶段
shouldComponentUpdate
:控制是否更新。getSnapshotBeforeUpdate
:捕捉更新前的 DOM 状态(如滚动位置)。componentDidUpdate
:更新完成后的回调。
-
卸载阶段
componentWillUnmount
:组件卸载前执行清理操作。
-
错误处理
componentDidCatch
:捕捉渲染期间的错误。getDerivedStateFromError
:用来降级 UI。
三、Function 组件生命周期 & Hooks
React Hooks 是对生命周期机制的重新设计,核心是用组合取代分阶段。
useEffect
替代多个生命周期
import { useEffect, useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
// 相当于 componentDidMount + componentDidUpdate
useEffect(() => {
console.log('副作用执行');
return () => {
console.log('清理副作用'); // 相当于 componentWillUnmount
};
}, [count]); // 依赖变更时执行
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
对应关系:
生命周期 | 对应 Hook |
---|---|
componentDidMount | useEffect(() => { ... }, []) |
componentDidUpdate | useEffect(() => { ... }, [deps]) |
componentWillUnmount | useEffect(() => { return () => {...} }, []) |
shouldComponentUpdate | React.memo + useCallback /useMemo |
四、React 18 的并发特性对生命周期的影响
React 18 引入了 Concurrent Mode(并发模式) 和 自动批处理,这对生命周期的执行顺序产生了影响。
变化点:
-
Strict Mode 下 useEffect 会执行两次(开发环境)
// React.StrictMode 会在开发模式下模拟卸载再重建 useEffect(() => { console.log('effect'); return () => { console.log('cleanup'); }; }, []);
这是为了提前发现副作用中的问题,生产环境只执行一次。
-
Transition 会延迟副作用执行
import { startTransition } from 'react'; startTransition(() => { setState("new"); }); // 此时 useEffect 可能会被延迟执行,提高响应性能
-
自动批处理
setCount(1); setFlag(true); // 在 React 18 中,这两个 setState 会被自动合并,提高性能。
五、实际开发场景下的使用建议
✅ 推荐做法(Function 组件)
- 初始数据拉取 →
useEffect(..., [])
- DOM 事件监听 →
useEffect
+ 清理 - 定时器、订阅等副作用 → 必须在
useEffect
中清理 - 页面卸载清理 →
useEffect
的返回函数 - 性能优化 →
useMemo
,useCallback
,React.memo
❌ 避免的误区
- 多个副作用混在一个
useEffect
里 → 可读性差,拆分多个 effect 更清晰 - 在
render
中执行副作用 → 会违反 React 的纯函数原则 - 依赖数组写错或遗漏 → 可使用 eslint-plugin-react-hooks 保障正确性
React 的生命周期机制并不是一成不变的规则,而是伴随 React 架构演进而不断升级的策略。从类组件的阶段式调用,到函数组件中用组合和 Hook 管理副作用,再到 React 18 的并发更新优化,我们应该拥抱变化,理解背后的设计哲学:
副作用是 UI 更新的结果,而非原因。