首先简单回顾一下官方文档对useEffect的定义:
useEffect 是一个 React Hook,作用是连接外部系统(显示在页面上但不受react控制的系统)
useEffect(setup, dependencies?)
setup 用来连接外部系统的代码,返回一个清理函数(cleanup),用来与该外部系统断开连接
模版:
useEffect(() => {
// 副作用逻辑(组件挂载或依赖项变化时执行)
return () => {
// 清理逻辑(组件卸载或依赖项变化前执行)
};
}, [dependencies]);
空依赖数组 []
挂载/卸载时执行,即Effect 仅在组件首次渲染后执行一次
场景
- 定时器(面试热点)
useEffect(() => {
const id = window.setInterval(() => {
setCount(prev => prev + 1);
}, 2000);
// 清除定时器,避免组件卸载后计时仍进行
return () => clearInterval(id);
}, []); // 空依赖数组:只启动一次间隔逻辑
- 初始化api请求
useEffect(() => {
// 组件挂载时调用 API 获取用户信息
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(setUser)
.catch(console.error);
// 无需 cleanup,因为只是一次请求
}, []); // 空依赖数组:只在首次渲染后运行一次
- 添加全局浏览器事件监听(如窗口大小
useEffect(() => {
const handleResize = () => {
setSize({ w: window.innerWidth, h: window.innerHeight });
};
window.addEventListener('resize', handleResize);
// cleanup:组件卸载时移除 listener
return () => {
window.removeEventListener('resize', handleResize);
};
}, []); // 确保 listener 只设置一次
还有如访问一次性dom元素、websocket订阅
含依赖项的数组 [dep1, dep2] - 条件执行
Effect 在依赖项发生变化时执行,清理函数在依赖项变化导致effect重新执行前运行
场景
- 表单验证
useEffect(() => {
const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
setIsValid(valid);
}, [email]); // 只在email变化时执行验证,依赖输入值
- 订阅更新
function MessageSubscriber({ channelId }) {
useEffect(() => {
// 1. 设置订阅:订阅指定频道的消息
const subscription = subscribe(channelId, msg => {
console.log("receive:", msg);
});
// 2. Cleanup:当组件卸载或 channelId 变化时取消订阅
return () => {
subscription.unsubscribe();
};
}, [channelId]);// channelId 改变时重建订阅,依赖外部id
}
- 数据过滤、排序
// 从后端拉取 items(只需一次)
useEffect(() => {
fetch('/api/items').then(res => res.json()).then(setItems);
}, []);
// 仅当 filter 或 items 改变时才应用过滤逻辑
useEffect(() => {
setFiltered(items.filter(item => item.type === filter));
}, [filter, items]);
无依赖数组undefined
每次渲染后都执行
useEffect(() => {
// 每次渲染后都执行(包括首次)
console.log('每次渲染都执行');
return () => {
// 清理函数在每次 effect 重新执行前运行
console.log('清理前一次 effect');
};
}); // 无第二个参数
场景
极少使用。最常用的场景:跟踪 DOM 元素尺寸或滚动位置等副作用依赖 UI 的变化
function DebugLogger({ value }) {
useEffect(() => {
console.log('组件渲染完成,当前 value:', value);
return () => {
console.log('即将重新渲染(或卸载),清理前一次 effect');
};
}); // 没有依赖数组:每次渲染都执行
return <div>{value}</div>;
}
性能开销大,不要滥用
总结
| 依赖数组 | 执行时机 | 清理函数触发时机 |
|---|---|---|
undefined | 每次渲染后 | 每次重新执行 effect 前 |
[] | 仅首次渲染后 | 组件卸载时 |
[dep1, dep2] | 依赖项变化时 | 依赖变化导致重新执行 effect 前 |
参考
- https://www.ruanyifeng.com/blog/2020/09/react-hooks-useeffect-tutorial.html
- https://zh-hans.react.dev/reference/react/useEffect
- https://juejin.cn/post/7034107004918431774
575

被折叠的 条评论
为什么被折叠?



