useEffect
的清除函数(Cleanup Function)是其核心特性之一,用于在副作用执行后清理资源,避免内存泄漏或不必要的副作用持续运行。以下是关于清除函数的详细介绍:
什么是清除函数?
清除函数是 useEffect
回调函数中返回的一个函数,用于清理当前副作用产生的资源。它的执行时机是:
- 组件卸载前(类似
componentWillUnmount
) - 下一次副作用执行前(当前依赖项变化时)
基本语法
useEffect(() => {
// 副作用逻辑(如注册事件、启动定时器等)
// 清除函数(可选)
return () => {
// 清理操作(如移除事件监听、清除定时器等)
};
}, [dependencies]);
执行时机详解
- 组件首次渲染:
- 先执行副作用逻辑
- 不执行清除函数(此时还没有需要清理的资源)
- 依赖项变化时:
- 先执行上一次副作用的清除函数
- 再执行本次的副作用逻辑
- 组件卸载时:
- 执行当前副作用的清除函数(最后一次清理)
常见使用场景
1. 清理事件监听
避免组件卸载后仍监听事件,导致内存泄漏:
useEffect(() => {
const handleScroll = () => {
console.log('滚动位置:', window.scrollY);
};
// 注册事件
window.addEventListener('scroll', handleScroll);
// 清除函数:移除事件监听
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []); // 空依赖 → 只注册一次
2. 清除定时器/间隔器
防止组件卸载后定时器仍在运行:
const [count, setCount] = useState(0);
useEffect(() => {
// 启动定时器
const timer = setInterval(() => {
setCount(prev => prev + 1);
}, 1000);
// 清除函数:停止定时器
return () => {
clearInterval(timer);
};
}, []); // 空依赖 → 只启动一次
3. 取消网络请求
避免组件卸载后请求仍在进行,导致无效的状态更新:
const [data, setData] = useState(null);
const [id, setId] = useState(1);
useEffect(() => {
// 创建请求控制器
const controller = new AbortController();
// 发送请求
const fetchData = async () => {
try {
const res = await fetch(`/api/data/${id}`, {
signal: controller.signal // 关联控制器
});
const result = await res.json();
setData(result);
} catch (err) {
if (err.name !== 'AbortError') {
console.error('请求失败:', err);
}
}
};
fetchData();
// 清除函数:取消请求
return () => {
controller.abort(); // 取消未完成的请求
};
}, [id]); // 依赖 id → id 变化时重新请求
4. 清理订阅
如 WebSocket 连接、状态管理订阅等:
useEffect(() => {
// 建立 WebSocket 连接
const socket = new WebSocket('wss://example.com');
socket.onmessage = (event) => {
console.log('收到消息:', event.data);
};
// 清除函数:关闭连接
return () => {
socket.close();
};
}, []);
关键特性
- 可选性:并非所有副作用都需要清除函数(如一次性的数据初始化)。
- 幂等性:清除函数应设计为可安全多次调用(避免因意外执行导致错误)。
- 与依赖项关联:当依赖项变化时,清除函数会先于新的副作用执行,确保资源无缝切换。
常见误区
- 忘记清理事件监听:导致组件卸载后事件仍被触发,执行已失效的逻辑。
- 清除函数中使用过时状态:由于闭包特性,清除函数只能访问定义时的状态值:
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCount(prev => prev + 1);
}, 1000);
// 此处的 count 始终是 0(定义时的初始值)
return () => {
console.log('清理时的 count:', count); // 始终输出 0
clearInterval(timer);
};
}, []);
总结
清除函数是 useEffect
中管理资源生命周期的关键机制,主要作用是:
- 防止内存泄漏(如未清理的事件监听、定时器)
- 避免无效操作(如组件卸载后的状态更新、网络请求)
- 确保副作用在依赖项变化时平滑切换
编写副作用逻辑时,应始终思考:“这个操作是否需要清理?”,尤其是涉及事件监听、定时器、网络连接等长期存在的资源时。