useEffect 的清除函数(Cleanup Function)是其核心特性之一,用于在副作用执行后清理资源,避免内存泄漏或不必要的副作用持续运行。以下是关于清除函数的详细介绍:

什么是清除函数?

清除函数是 useEffect 回调函数中返回的一个函数,用于清理当前副作用产生的资源。它的执行时机是:

  • 组件卸载前(类似 componentWillUnmount
  • 下一次副作用执行前(当前依赖项变化时)

基本语法

useEffect(() => {// 副作用逻辑(如注册事件、启动定时器等)// 清除函数(可选)return () => {// 清理操作(如移除事件监听、清除定时器等)};
}, [dependencies]);

执行时机详解

  1. 组件首次渲染
  • 先执行副作用逻辑
  • 不执行清除函数(此时还没有需要清理的资源)
  1. 依赖项变化时
  • 先执行上一次副作用的清除函数
  • 再执行本次的副作用逻辑
  1. 组件卸载时
  • 执行当前副作用的清除函数(最后一次清理)

常见使用场景

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); // 始终输出 0clearInterval(timer);};
}, []);

总结

清除函数是 useEffect 中管理资源生命周期的关键机制,主要作用是:

  • 防止内存泄漏(如未清理的事件监听、定时器)
  • 避免无效操作(如组件卸载后的状态更新、网络请求)
  • 确保副作用在依赖项变化时平滑切换

编写副作用逻辑时,应始终思考:“这个操作是否需要清理?”,尤其是涉及事件监听、定时器、网络连接等长期存在的资源时。