useEffect Hook 生命周期模拟
用 useEffect 模拟 componentDidMount、componentDidUpdate 和 componentWillUnmount
问题
如何用 useEffect 模拟类组件的生命周期方法?
解答
模拟 componentDidMount
组件挂载时执行一次,依赖数组传空数组。
import { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// 组件挂载后执行,只执行一次
console.log('componentDidMount');
// 例如:获取数据、订阅事件、操作 DOM
fetchData();
}, []); // 空依赖数组
return <div>Hello</div>;
}
模拟 componentDidUpdate
监听特定状态变化,依赖数组传入要监听的值。
import { useEffect, useState, useRef } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
const isFirstRender = useRef(true);
// 方式一:包含首次渲染
useEffect(() => {
console.log('count 变化了', count);
}, [count]);
// 方式二:排除首次渲染(更接近 componentDidUpdate)
useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
return;
}
console.log('componentDidUpdate: count =', count);
}, [count]);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}
模拟 componentWillUnmount
返回清理函数,组件卸载时执行。
import { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
const timer = setInterval(() => {
console.log('tick');
}, 1000);
// 返回清理函数,组件卸载时执行
return () => {
console.log('componentWillUnmount');
clearInterval(timer);
};
}, []);
return <div>Timer Running</div>;
}
完整示例
import { useEffect, useState, useRef } from 'react';
function LifecycleDemo({ userId }) {
const [user, setUser] = useState(null);
const isFirstRender = useRef(true);
// componentDidMount
useEffect(() => {
console.log('组件挂载');
return () => {
console.log('组件卸载');
};
}, []);
// componentDidUpdate (监听 userId 变化)
useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
return;
}
console.log('userId 更新为:', userId);
}, [userId]);
// 数据获取(挂载 + userId 变化时)
useEffect(() => {
let cancelled = false;
async function fetchUser() {
const res = await fetch(`/api/users/${userId}`);
const data = await res.json();
// 防止组件卸载后更新状态
if (!cancelled) {
setUser(data);
}
}
fetchUser();
return () => {
cancelled = true;
};
}, [userId]);
return <div>{user?.name}</div>;
}
关键点
- 空依赖数组
[]:effect 只在挂载时执行一次,模拟componentDidMount - 有依赖数组
[dep]:依赖变化时执行,模拟componentDidUpdate - 返回函数:组件卸载或依赖变化前执行,模拟
componentWillUnmount - 跳过首次渲染:用
useRef标记是否首次渲染,实现纯粹的componentDidUpdate - 清理副作用:定时器、订阅、请求等都需要在返回函数中清理
目录