类组件生命周期三个阶段:
挂载阶段:
constructor → getDerivedStateFromProps → render → componentDidMount
更新阶段:
getDerivedStateFromProps → shouldComponentUpdate → render →
getSnapshotBeforeUpdate → componentDidUpdate
卸载阶段:
componentWillUnmount
错误处理:
getDerivedStateFromError → componentDidCatch
组件实例化时调用,用于初始化 state 和绑定方法。
class MyComponent extends React.Component {
constructor(props) {
super(props); // 必须调用 super(props)
// 初始化 state
this.state = {
count: 0,
data: null,
};
// 绑定事件处理方法
this.handleClick = this.handleClick.bind(this);
this.handleChange = this.handleChange.bind(this);
console.log("constructor 被调用");
}
handleClick() {
// 处理点击
}
handleChange() {
// 处理变化
}
render() {
return <div>组件内容</div>;
}
}
在组件实例化或接收新 props 时调用,返回一个对象来更新 state,或返回 null 不更新。
class MyComponent extends React.Component {
state = {
derivedValue: "",
};
static getDerivedStateFromProps(nextProps, prevState) {
console.log("getDerivedStateFromProps 被调用");
console.log("新的 props:", nextProps);
console.log("当前的 state:", prevState);
// 根据 props 计算派生 state
if (nextProps.value !== prevState.lastValue) {
return {
derivedValue: nextProps.value.toUpperCase(),
lastValue: nextProps.value,
};
}
return null; // 不更新 state
}
render() {
return <div>派生值: {this.state.derivedValue}</div>;
}
}
必须实现的方法,返回 JSX。
class MyComponent extends React.Component {
render() {
console.log("render 被调用");
// 不能在这里调用 setState()
// 应该是一个纯函数
return (
<div>
<h1>标题</h1>
<p>计数: {this.state.count}</p>
<button onClick={this.handleClick}>点击</button>
</div>
);
}
}
组件挂载到 DOM 后立即调用,适合执行副作用操作。
class MyComponent extends React.Component {
state = {
data: null,
loading: true,
error: null,
};
async componentDidMount() {
console.log("componentDidMount 被调用");
try {
// 1. 发起网络请求
const response = await fetch("https://api.example.com/data");
const data = await response.json();
this.setState({
data,
loading: false,
});
// 2. 设置定时器
this.timerId = setInterval(() => {
console.log("定时器执行");
}, 1000);
// 3. 添加事件监听器
window.addEventListener("resize", this.handleResize);
// 4. 操作 DOM
this.containerRef.current.focus();
// 5. 初始化第三方库
this.chart = new Chart(this.canvasRef.current, config);
} catch (error) {
this.setState({
error: error.message,
loading: false,
});
}
}
handleResize = () => {
console.log("窗口大小改变");
};
render() {
// render 方法
}
}
在更新阶段也会调用(同挂载阶段)。
决定组件是否需要重新渲染,返回布尔值。
class MyComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
console.log("shouldComponentUpdate 被调用");
// 性能优化:只有当特定 props 或 state 改变时才重新渲染
if (this.props.id !== nextProps.id) {
return true;
}
if (this.state.count !== nextState.count) {
return true;
}
// 其他情况不重新渲染
return false;
// 或者使用浅比较
// return !shallowEqual(this.props, nextProps) ||
// !shallowEqual(this.state, nextState);
}
render() {
return <div>组件内容</div>;
}
}
// React.PureComponent 自动实现了浅比较的 shouldComponentUpdate
class PureComponentExample extends React.PureComponent {
// 自动比较 props 和 state 的变化
render() {
return <div>PureComponent</div>;
}
}
重新渲染组件。
在 DOM 更新前获取快照,返回值会作为 componentDidUpdate 的第三个参数。
class ScrollList extends React.Component {
state = {
items: [],
};
listRef = React.createRef();
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("getSnapshotBeforeUpdate 被调用");
// 获取滚动位置
const list = this.listRef.current;
if (prevState.items.length < this.state.items.length) {
// 新项目添加到列表顶部,记录当前滚动位置
return list.scrollHeight - list.scrollTop;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("componentDidUpdate 被调用,快照:", snapshot);
if (snapshot !== null) {
const list = this.listRef.current;
// 保持滚动位置
list.scrollTop = list.scrollHeight - snapshot;
}
}
addItem = () => {
this.setState(prevState => ({
items: [`新项目 ${Date.now()}`, ...prevState.items],
}));
};
render() {
return (
<div>
<button onClick={this.addItem}>添加项目</button>
<div ref={this.listRef} style={{ height: "200px", overflow: "auto" }}>
{this.state.items.map((item, index) => (
<div
key={index}
style={{ padding: "10px", borderBottom: "1px solid #ccc" }}
>
{item}
</div>
))}
</div>
</div>
);
}
}
组件更新后立即调用,适合执行 DOM 操作或网络请求。
class MyComponent extends React.Component {
state = {
userData: null,
};
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("componentDidUpdate 被调用");
// 比较 props 变化
if (this.props.userId !== prevProps.userId) {
// 用户ID改变,重新获取数据
this.fetchUserData(this.props.userId);
}
// 比较 state 变化
if (this.state.searchQuery !== prevState.searchQuery) {
// 搜索条件改变,重新搜索
this.performSearch(this.state.searchQuery);
}
// 使用快照
if (snapshot) {
console.log("从 getSnapshotBeforeUpdate 获取的快照:", snapshot);
}
// 更新第三方库
if (this.chart && this.props.data !== prevProps.data) {
this.chart.update(this.props.data);
}
// 发送分析数据
if (this.state.pageViewCount !== prevState.pageViewCount) {
analytics.track("page_view", { count: this.state.pageViewCount });
}
}
fetchUserData = async userId => {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
this.setState({ userData: data });
};
render() {
// render 方法
}
}
组件卸载前调用,用于清理操作。
class MyComponent extends React.Component {
state = {
timerId: null,
subscription: null,
connection: null,
};
async componentDidMount() {
// 1. 设置定时器
const timerId = setInterval(() => {
console.log("定时执行");
}, 1000);
this.setState({ timerId });
// 2. 订阅事件
const subscription = eventBus.subscribe("dataUpdate", this.handleDataUpdate);
this.setState({ subscription });
// 3. 建立 WebSocket 连接
const connection = new WebSocket("ws://example.com");
connection.onmessage = this.handleMessage;
this.setState({ connection });
// 4. 添加事件监听器
window.addEventListener("resize", this.handleResize);
// 5. 初始化第三方库
this.map = new Map(this.mapContainer.current, options);
}
componentWillUnmount() {
console.log("componentWillUnmount 被调用");
// 1. 清除定时器
if (this.state.timerId) {
clearInterval(this.state.timerId);
}
// 2. 取消订阅
if (this.state.subscription) {
this.state.subscription.unsubscribe();
}
// 3. 关闭连接
if (this.state.connection) {
this.state.connection.close();
}
// 4. 移除事件监听器
window.removeEventListener("resize", this.handleResize);
// 5. 清理第三方库
if (this.map) {
this.map.destroy();
}
// 6. 取消未完成的网络请求
if (this.controller) {
this.controller.abort();
}
// 7. 清理 refs
this.containerRef = null;
// 8. 重置状态(虽然 React 会处理,但某些情况下可能需要)
this.setState = () => {};
}
handleResize = () => {
// 处理窗口大小改变
};
render() {
return <div ref={this.mapContainer} />;
}
}
在子组件抛出错误后调用,用于渲染降级 UI。
class ErrorBoundary extends React.Component {
state = {
hasError: false,
error: null,
errorInfo: null,
};
static getDerivedStateFromError(error) {
console.log("getDerivedStateFromError 被调用");
// 更新 state 以显示降级 UI
return {
hasError: true,
error: error,
};
}
componentDidCatch(error, errorInfo) {
console.log("componentDidCatch 被调用");
// 可以在这里记录错误信息
console.error("错误:", error);
console.error("错误信息:", errorInfo);
// 发送错误到监控服务
logErrorToService(error, errorInfo);
this.setState({
errorInfo: errorInfo,
});
}
resetError = () => {
this.setState({
hasError: false,
error: null,
errorInfo: null,
});
};
render() {
if (this.state.hasError) {
// 降级 UI
return (
<div className="error-boundary">
<h2>出错了!</h2>
<p>{this.state.error && this.state.error.toString()}</p>
<details style={{ whiteSpace: "pre-wrap" }}>
{this.state.errorInfo && this.state.errorInfo.componentStack}
</details>
<button onClick={this.resetError}>重试</button>
</div>
);
}
// 正常渲染子组件
return this.props.children;
}
}
// 使用方式
function App() {
return (
<ErrorBoundary>
<BuggyComponent />
</ErrorBoundary>
);
}
在子组件抛出错误后调用,用于记录错误信息。
// ❌ 已废弃
componentWillMount() {
console.log('componentWillMount - 已废弃');
// 不推荐在这里发起网络请求
}
// ✅ 替代方案:使用 constructor 或 componentDidMount
constructor(props) {
super(props);
// 初始化工作
}
componentDidMount() {
// 发起网络请求
}
// ❌ 已废弃
componentWillReceiveProps(nextProps) {
if (this.props.userId !== nextProps.userId) {
this.fetchData(nextProps.userId);
}
}
// ✅ 替代方案:使用 getDerivedStateFromProps 或 componentDidUpdate
static getDerivedStateFromProps(nextProps, prevState) {
// 派生 state
}
componentDidUpdate(prevProps) {
if (this.props.userId !== prevProps.userId) {
this.fetchData(this.props.userId);
}
}
// ❌ 已废弃
componentWillUpdate(nextProps, nextState) {
// 准备更新
}
// ✅ 替代方案:使用 getSnapshotBeforeUpdate
getSnapshotBeforeUpdate(prevProps, prevState) {
// 获取 DOM 更新前的快照
}
import { useState, useEffect, useRef } from "react";
function FunctionComponentLifecycle(props) {
const [count, setCount] = useState(0);
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const prevCountRef = useRef();
const prevPropsRef = useRef(props);
// 模拟 constructor
const initialState = () => {
console.log("模拟 constructor - 只执行一次");
return 0;
};
const [stateValue, setStateValue] = useState(() => initialState());
// 模拟 componentDidMount
useEffect(() => {
console.log("模拟 componentDidMount - 只执行一次");
// 发起网络请求
fetchData();
// 设置定时器
const timerId = setInterval(() => {
console.log("定时器执行");
}, 1000);
// 添加事件监听器
window.addEventListener("resize", handleResize);
// 清理函数 - 模拟 componentWillUnmount
return () => {
console.log("模拟 componentWillUnmount");
clearInterval(timerId);
window.removeEventListener("resize", handleResize);
};
}, []); // 空依赖数组表示只运行一次
// 模拟 componentDidUpdate(特定 state/props 变化时)
useEffect(() => {
console.log("模拟 componentDidUpdate - count 变化:", count);
// 可以执行副作用操作
document.title = `计数: ${count}`;
// 发送分析数据
if (prevCountRef.current !== undefined) {
console.log(`计数从 ${prevCountRef.current} 变为 ${count}`);
}
// 更新 ref
prevCountRef.current = count;
}, [count]); // 依赖 count,count 变化时执行
// 模拟 getDerivedStateFromProps
useEffect(() => {
if (prevPropsRef.current.userId !== props.userId) {
console.log("props.userId 变化,重新获取数据");
fetchUserData(props.userId);
}
prevPropsRef.current = props;
}, [props.userId]);
// 模拟 getSnapshotBeforeUpdate + componentDidUpdate
const prevCount = useRef(count);
useEffect(() => {
// 在渲染后执行,类似于 componentDidUpdate
console.log(`计数更新: ${prevCount.current} -> ${count}`);
if (prevCount.current !== count) {
// 这里可以访问到更新后的 DOM
console.log("DOM 已更新");
}
prevCount.current = count;
});
// 获取数据函数
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch("https://api.example.com/data");
const result = await response.json();
setData(result);
} catch (error) {
console.error("获取数据失败:", error);
} finally {
setLoading(false);
}
};
const fetchUserData = async userId => {
// 获取用户数据
};
const handleResize = () => {
console.log("窗口大小改变");
};
const handleClick = () => {
setCount(count + 1);
};
// 模拟 render
console.log("模拟 render - 每次更新都执行");
if (loading) {
return <div>加载中...</div>;
}
return (
<div>
<h1>函数组件生命周期示例</h1>
<p>计数: {count}</p>
<button onClick={handleClick}>增加</button>
<p>数据: {JSON.stringify(data)}</p>
</div>
);
}
import { useEffect, useRef } from "react";
// 自定义 Hook:模拟 componentDidMount
function useDidMount(callback) {
useEffect(() => {
callback();
}, []); // 空数组确保只执行一次
}
// 自定义 Hook:模拟 componentDidUpdate
function useDidUpdate(callback, dependencies) {
const hasMounted = useRef(false);
useEffect(() => {
if (hasMounted.current) {
callback();
} else {
hasMounted.current = true;
}
}, dependencies);
}
// 自定义 Hook:模拟 componentWillUnmount
function useWillUnmount(callback) {
useEffect(() => {
return callback;
}, []);
}
// 使用示例
function ComponentWithCustomHooks() {
useDidMount(() => {
console.log("组件已挂载");
});
useDidUpdate(() => {
console.log("组件已更新");
}, []); // 依赖项
useWillUnmount(() => {
console.log("组件即将卸载");
});
return <div>组件内容</div>;
}
class DataFetcher extends React.Component {
state = {
data: null,
loading: true,
error: null,
};
controller = new AbortController(); // 用于取消请求
async componentDidMount() {
await this.fetchData();
}
async componentDidUpdate(prevProps) {
if (this.props.query !== prevProps.query) {
// 取消之前的请求
this.controller.abort();
this.controller = new AbortController();
await this.fetchData();
}
}
componentWillUnmount() {
// 组件卸载时取消请求
this.controller.abort();
}
async fetchData() {
try {
this.setState({ loading: true, error: null });
const response = await fetch(`/api/data?q=${this.props.query}`, {
signal: this.controller.signal,
});
if (!response.ok) throw new Error("请求失败");
const data = await response.json();
this.setState({ data, loading: false });
} catch (error) {
if (error.name !== "AbortError") {
this.setState({ error: error.message, loading: false });
}
}
}
render() {
// 渲染逻辑
}
}
class OptimizedComponent extends React.PureComponent {
// 使用 PureComponent 自动浅比较
// 避免在 render 中创建新对象/函数
get optimizedData() {
// 计算属性
return this.props.data.filter(item => item.active);
}
// 使用箭头函数避免绑定
handleClick = () => {
// 处理点击
};
// 复杂计算使用 memoization
memoizedValue = null;
lastComputeProps = null;
getComputedValue() {
if (this.lastComputeProps !== this.props.complexProp) {
this.memoizedValue = expensiveComputation(this.props.complexProp);
this.lastComputeProps = this.props.complexProp;
}
return this.memoizedValue;
}
render() {
const computedValue = this.getComputedValue();
return (
<div>
{this.optimizedData.map(item => (
<Item key={item.id} item={item} onClick={this.handleClick} />
))}
</div>
);
}
}
class ChartComponent extends React.Component {
chart = null;
containerRef = React.createRef();
componentDidMount() {
// 初始化图表
this.initChart();
}
componentDidUpdate(prevProps) {
// 数据变化时更新图表
if (this.props.data !== prevProps.data) {
this.updateChart();
}
// 配置变化时重新初始化
if (this.props.options !== prevProps.options) {
this.destroyChart();
this.initChart();
}
}
componentWillUnmount() {
// 清理图表
this.destroyChart();
}
initChart() {
this.chart = new Chart(this.containerRef.current, {
type: "line",
data: this.props.data,
options: this.props.options,
});
}
updateChart() {
if (this.chart) {
this.chart.data = this.props.data;
this.chart.update();
}
}
destroyChart() {
if (this.chart) {
this.chart.destroy();
this.chart = null;
}
}
render() {
return <div ref={this.containerRef} />;
}
}
class CommonMistakes extends React.Component {
// ❌ 错误:在 constructor 中基于 props 设置 state
constructor(props) {
super(props);
this.state = {
value: props.initialValue,
};
}
// 问题:当 props 变化时,state 不会更新
// ✅ 正确:使用 getDerivedStateFromProps
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.initialValue !== prevState.prevInitialValue) {
return {
value: nextProps.initialValue,
prevInitialValue: nextProps.initialValue,
};
}
return null;
}
// ❌ 错误:在 componentDidUpdate 中无条件调用 setState
componentDidUpdate() {
this.setState({ updated: true }); // 导致无限循环
}
// ✅ 正确:添加条件判断
componentDidUpdate(prevProps) {
if (this.props.value !== prevProps.value) {
this.setState({ updated: true });
}
}
// ❌ 错误:在 render 中调用 setState
render() {
// this.setState({}); // 会导致无限循环
return <div />;
}
// ❌ 错误:在 componentWillUnmount 中调用 setState
componentWillUnmount() {
// this.setState({}); // 组件已卸载,无法设置状态
}
}
class ParentComponent extends React.Component {
constructor(props) {
super(props);
console.log("Parent: constructor");
this.state = { count: 0 };
}
static getDerivedStateFromProps(nextProps, prevState) {
console.log("Parent: getDerivedStateFromProps");
return null;
}
componentDidMount() {
console.log("Parent: componentDidMount");
}
shouldComponentUpdate(nextProps, nextState) {
console.log("Parent: shouldComponentUpdate");
return true;
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("Parent: getSnapshotBeforeUpdate");
return "snapshot";
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("Parent: componentDidUpdate, 快照:", snapshot);
}
componentWillUnmount() {
console.log("Parent: componentWillUnmount");
}
handleClick = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
console.log("Parent: render");
return (
<div>
<h1>父组件</h1>
<button onClick={this.handleClick}>更新</button>
<ChildComponent count={this.state.count} />
</div>
);
}
}
class ChildComponent extends React.Component {
constructor(props) {
super(props);
console.log("Child: constructor");
}
static getDerivedStateFromProps(nextProps, prevState) {
console.log("Child: getDerivedStateFromProps");
return null;
}
componentDidMount() {
console.log("Child: componentDidMount");
}
// ... 其他生命周期方法
render() {
console.log("Child: render");
return <div>子组件,计数: {this.props.count}</div>;
}
}
// 控制台输出顺序:
// 挂载时:
// Parent: constructor
// Parent: getDerivedStateFromProps
// Parent: render
// Child: constructor
// Child: getDerivedStateFromProps
// Child: render
// Child: componentDidMount
// Parent: componentDidMount
// 更新时(点击按钮):
// Parent: getDerivedStateFromProps
// Parent: shouldComponentUpdate
// Parent: render
// Child: getDerivedStateFromProps
// Child: shouldComponentUpdate
// Child: render
// Parent: getSnapshotBeforeUpdate
// Child: getSnapshotBeforeUpdate
// Child: componentDidUpdate
// Parent: componentDidUpdate