React 生命周期完整指南
1. 生命周期概述
1.1 React 16.3 之前的生命周期
- 初始化阶段
- constructor
- componentWillMount
- render
- componentDidMount
- 更新阶段
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
- render
- componentDidUpdate
- 卸载阶段
- componentWillUnmount
1.2 React 16.3 之后的生命周期
- 初始化阶段
- constructor
- getDerivedStateFromProps
- render
- componentDidMount
- 更新阶段
- getDerivedStateFromProps
- shouldComponentUpdate
- render
- getSnapshotBeforeUpdate
- componentDidUpdate
- 卸载阶段
- componentWillUnmount
2. 详细说明
2.1 挂载阶段
jsx
class MyComponent extends React.Component {
// 1. 构造函数
constructor(props) {
super(props);
this.state = {
count: 0
};
console.log('1. constructor');
}
// 2. 派生状态
static getDerivedStateFromProps(props, state) {
console.log('2. getDerivedStateFromProps');
return null;
}
// 3. 渲染
render() {
console.log('3. render');
return <div>{this.state.count}</div>;
}
// 4. 挂载完成
componentDidMount() {
console.log('4. componentDidMount');
}
}
2.2 更新阶段
jsx
class UpdateComponent extends React.Component {
// 1. 派生状态
static getDerivedStateFromProps(props, state) {
console.log('1. getDerivedStateFromProps');
return null;
}
// 2. 是否应该更新
shouldComponentUpdate(nextProps, nextState) {
console.log('2. shouldComponentUpdate');
return true;
}
// 3. 渲染
render() {
console.log('3. render');
return <div>{this.state.count}</div>;
}
// 4. 获取更新前的快照
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log('4. getSnapshotBeforeUpdate');
return null;
}
// 5. 更新完成
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('5. componentDidUpdate');
}
}
2.3 卸载阶段
jsx
class UnmountComponent extends React.Component {
componentWillUnmount() {
console.log('componentWillUnmount');
// 清理工作:取消订阅、清除定时器等
}
}
3. 新旧生命周期对比
3.1 被移除的生命周期方法
componentWillMount
jsx
// 旧版本
componentWillMount() {
// 组件即将挂载
// 问题:可能会导致服务器端渲染问题
}
// 新版本替代方案
componentDidMount() {
// 组件已挂载
// 建议在这里进行异步操作
}
componentWillReceiveProps
jsx
// 旧版本
componentWillReceiveProps(nextProps) {
// 即将接收新的 props
if (nextProps.value !== this.props.value) {
this.setState({ value: nextProps.value });
}
}
// 新版本替代方案
static getDerivedStateFromProps(props, state) {
if (props.value !== state.value) {
return { value: props.value };
}
return null;
}
componentWillUpdate
jsx
// 旧版本
componentWillUpdate(nextProps, nextState) {
// 即将更新
// 问题:可能会导致异步渲染问题
}
// 新版本替代方案
getSnapshotBeforeUpdate(prevProps, prevState) {
// 获取更新前的快照
return null;
}
3.2 新增的生命周期方法
getDerivedStateFromProps
jsx
class Example extends React.Component {
static getDerivedStateFromProps(props, state) {
// 返回新状态或 null
if (props.currentRow !== state.lastRow) {
return {
isScrollingDown: props.currentRow > state.lastRow,
lastRow: props.currentRow
};
}
return null;
}
}
getSnapshotBeforeUpdate
jsx
class ScrollingList extends React.Component {
getSnapshotBeforeUpdate(prevProps, prevState) {
// 捕获更新前的信息
if (prevProps.list.length < this.props.list.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// 使用快照信息
if (snapshot !== null) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}
}
4. 生命周期最佳实践
4.1 常见用途
jsx
class BestPractices extends React.Component {
constructor(props) {
super(props);
// 初始化状态
// 绑定方法
}
static getDerivedStateFromProps(props, state) {
// 用于状态的派生
// 不要在这里执行副作用
return null;
}
componentDidMount() {
// 适合执行副作用
// - 网络请求
// - DOM 操作
// - 订阅
}
shouldComponentUpdate(nextProps, nextState) {
// 性能优化
// 返回 false 可以阻止更新
return true;
}
getSnapshotBeforeUpdate(prevProps, prevState) {
// 获取 DOM 更新前的信息
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// 更新后的操作
// 可以执行副作用
// 注意避免无限循环
}
componentWillUnmount() {
// 清理工作
// - 取消订阅
// - 清除定时器
// - 取消网络请求
}
}
4.2 错误处理
jsx
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
// 更新状态,下次渲染时显示降级 UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 记录错误信息
console.error('Error:', error);
console.error('Error Info:', errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
5. Hooks 时代的生命周期
5.1 使用 Hooks 模拟生命周期
jsx
function HooksLifecycle() {
// 模拟 constructor
const [state, setState] = useState({
count: 0
});
// 模拟 componentDidMount
useEffect(() => {
console.log('Component mounted');
return () => {
// 模拟 componentWillUnmount
console.log('Component will unmount');
};
}, []);
// 模拟 componentDidUpdate
useEffect(() => {
console.log('State updated:', state);
}, [state]);
return <div>{state.count}</div>;
}
5.2 生命周期方法与 Hooks 对照表
生命周期方法 | Hooks 实现 |
---|---|
constructor | useState |
componentDidMount | useEffect(() => {}, []) |
componentDidUpdate | useEffect(() => {}, [deps]) |
componentWillUnmount | useEffect(() => { return () => {} }, []) |
shouldComponentUpdate | useMemo, useCallback |
getDerivedStateFromProps | useState + useEffect |
6. 总结
6.1 生命周期选择建议
- 使用
componentDidMount
进行初始化操作 - 使用
componentDidUpdate
响应更新 - 使用
componentWillUnmount
清理资源 - 优先考虑新的生命周期方法
- 在合适的场景使用 Hooks
6.2 注意事项
- 避免在构造函数中执行副作用
- 不要在
getDerivedStateFromProps
中执行副作用 - 谨慎使用
shouldComponentUpdate
- 在
componentDidUpdate
中注意避免无限循环 - 确保在
componentWillUnmount
中清理所有副作用
6.3函数组件中的生命周期(通过 Hooks)
useEffect 可以模拟 componentDidMount 和 componentDidUpdate,通过将依赖项传递给 useEffect 来控制副作用的执行。传递空数组 [] 作为依赖项时,useEffect 只会在组件首次挂载时执行,相当于 componentDidMount。
清理副作用:useEffect 可以返回一个清理函数,相当于类组件中的 componentWillUnmount。
import React, { useState, useEffect } from 'react';
const MyFunctionComponent = () => {
const [counter, setCounter] = useState(0);
// 模拟 componentDidMount 和 componentDidUpdate
useEffect(() => {
console.log('组件已挂载或更新');
// 模拟 componentWillUnmount
return () => {
console.log('组件将卸载');
};
}, [counter]); // 依赖项,只有当 counter 更新时,才会重新执行 useEffect
return (
<div>
<p>Counter: {counter}</p>
<button onClick={() => setCounter(counter + 1)}>增加计数</button>
</div>
);
};
export default MyFunctionComponent;
6.4 React 18 的新特性
React 18 引入了多个新特性,最显著的之一是 并发模式(Concurrent Mode),它改善了渲染性能,尤其是在 React 应用的响应式方面。虽然并发模式与生命周期方法的直接关系不大,但它会影响组件的挂载、更新和卸载过程。
React 18 还引入了 自动批量更新(Automatic Batching)和 Suspense 进一步增强了异步渲染的能力。这些新特性和生命周期方法并没有冲突,而是增强了现有的机制。
总结:
类组件:React 18 中,类组件的生命周期方法仍然有效,且与之前的版本一样。
函数组件:函数组件使用 useEffect 和其他 Hooks 来模拟传统的生命周期方法。
React 18 新特性:引入了并发模式、自动批量更新等,但它不会影响生命周期方法。
因此,如果你使用类组件,React 18 中依然可以使用传统的生命周期方法。如果使用函数组件,你可以通过 useEffect 来替代和模拟传统生命周期的行为。