React 的 useEffect 清理函数详解

React 的 useEffect 清理函数详解

useEffect 是 React 中用于处理副作用(side effects)的 Hook,清理函数(Cleanup Function)是 useEffect 中返回的一个函数,用于清理或撤销副作用。清理函数的主要目的是确保副作用不会在组件卸载或依赖项更新时造成问题,比如内存泄漏或不必要的逻辑执行。


清理函数的基本语法

复制代码
useEffect(() => {
  // 副作用逻辑
  console.log("Effect logic");

  // 返回清理函数
  return () => {
    console.log("Cleanup logic");
  };
}, [dependencies]); // 依赖项
工作机制
  • 初始执行

    • 当组件首次渲染时,useEffect 的副作用逻辑会执行。
    • 此时,清理函数不会被调用。
  • 依赖项更新时

    • 如果依赖项(dependencies)发生变化,React 会先调用清理函数,然后再重新执行新的副作用逻辑。
  • 组件卸载时

    • 当组件被卸载时,清理函数会被调用以撤销副作用。

清理函数的典型用途

清理函数的主要用途是清除或撤销在 useEffect 中创建的副作用,例如:

  1. 移除事件监听器
  2. 取消网络请求或定时器
  3. 清理订阅(如 WebSocket 或 Redux 订阅)
  4. 释放内存资源

举例说明

1. 清除事件监听器

如果在 useEffect 中添加了事件监听器,清理函数可以确保组件卸载时移除事件监听器。

复制代码
javascript
import React, { useEffect } from "react";

function Example() {
  useEffect(() => {
    const handleResize = () => {
      console.log("Window resized");
    };

    // 添加事件监听器
    window.addEventListener("resize", handleResize);

    // 清理函数:移除事件监听器
    return () => {
      window.removeEventListener("resize", handleResize);
    };
  }, []); // 空依赖数组,表示只在组件挂载和卸载时执行

  return <div>Resize the window to see logs in the console</div>;
}
工作流程:
  1. 组件挂载时
    • 添加 resize 事件监听器。
  2. 组件卸载时
    • 移除 resize 事件监听器,防止内存泄漏。

2. 清除定时器

如果在 useEffect 中设置了定时器,清理函数可以确保组件卸载时清除定时器。

复制代码
import React, { useState, useEffect } from "react";

function Timer() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCount((prev) => prev + 1);
    }, 1000);

    // 清理函数:清除定时器
    return () => {
      clearInterval(interval);
    };
  }, []); // 空依赖数组,表示只在组件挂载和卸载时执行

  return <div>Count: {count}</div>;
}
工作流程:
  1. 组件挂载时
    • 设置定时器,每秒更新 count
  2. 组件卸载时
    • 清除定时器,防止组件卸载后继续更新状态。

3. 清理订阅

如果在 useEffect 中订阅了某些外部数据源(如 WebSocket 或 Redux),清理函数可以确保在组件卸载时取消订阅。

复制代码
import React, { useEffect } from "react";

function WebSocketComponent() {
  useEffect(() => {
    const socket = new WebSocket("wss://example.com/socket");

    socket.onmessage = (event) => {
      console.log("Message from server:", event.data);
    };

    // 清理函数:关闭 WebSocket 连接
    return () => {
      socket.close();
    };
  }, []); // 空依赖数组,表示只在组件挂载和卸载时执行

  return <div>WebSocket Component</div>;
}
工作流程:
  1. 组件挂载时
    • 创建 WebSocket 连接。
    • 监听消息。
  2. 组件卸载时
    • 关闭 WebSocket 连接,释放资源。

4. 依赖项更新时的清理

如果 useEffect 的依赖项发生变化,清理函数会在旧的依赖被替换前执行。

复制代码
import React, { useState, useEffect } from "react";

function DependencyExample() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log(`Effect triggered for count: ${count}`);

    return () => {
      console.log(`Cleanup for count: ${count}`);
    };
  }, [count]); // 依赖 count

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount((prev) => prev + 1)}>Increment</button>
    </div>
  );
}
工作流程:
  1. 初始 count = 0
    • useEffect 执行,输出 Effect triggered for count: 0
  2. count 更新为 1
    • 清理函数执行,输出 Cleanup for count: 0
    • 然后,useEffect 执行,输出 Effect triggered for count: 1

清理函数的使用场景总结

场景 示例 清理函数作用
事件监听器 window.addEventListener 移除事件监听器,防止内存泄漏
定时器 setIntervalsetTimeout 清除定时器,防止组件卸载后继续执行
订阅 WebSocket、Redux 订阅 取消订阅,释放资源
依赖项更新 动态依赖项 在依赖项变化时清理旧的副作用
外部库实例化或初始化 地图、图表库等初始化 销毁外部库实例,防止内存泄漏

useEffect 的清理函数运行时机

  1. 组件卸载时

    • 当组件被移除时,清理函数会被执行一次。
  2. 依赖项更新时

    • 如果 useEffect 的依赖项发生变化,React 会先执行清理函数,再执行新的副作用逻辑。
  3. React 渲染优化

    • 如果组件的依赖项没有变化,React 不会重新执行 useEffect,也不会调用清理函数。

最佳实践

  1. 始终清理副作用

    • 确保在 useEffect 中创建的任何副作用(如事件监听器、定时器、订阅)都在清理函数中正确清除。
  2. 避免过度依赖外部资源

    • 如果 useEffect 中使用了外部变量,确保它们被正确声明为依赖项,避免意外行为。
  3. 理解依赖项行为

    • 确保依赖项的正确性,避免遗漏或过多依赖项,导致副作用重复执行。
相关推荐
军军君0124 分钟前
Three.js基础功能学习十三:太阳系实例上
前端·javascript·vue.js·学习·3d·前端框架·three
xiaoqi9221 小时前
React Native鸿蒙跨平台如何实现分类页面组件通过searchQuery状态变量管理搜索输入,实现了分类的实时过滤功能
javascript·react native·react.js·ecmascript·harmonyos
打小就很皮...2 小时前
Tesseract.js OCR 中文识别
前端·react.js·ocr
qq_177767372 小时前
React Native鸿蒙跨平台实现应用介绍页,实现了应用信息卡片展示、特色功能网格布局、权限/联系信息陈列、评分展示、模态框详情交互等通用场景
javascript·react native·react.js·ecmascript·交互·harmonyos
wuhen_n2 小时前
JavaScript内存管理与执行上下文
前端·javascript
Hi_kenyon2 小时前
理解vue中的ref
前端·javascript·vue.js
jin1233223 小时前
基于React Native鸿蒙跨平台地址管理是许多电商、外卖、物流等应用的重要功能模块,实现了地址的添加、编辑、删除和设置默认等功能
javascript·react native·react.js·ecmascript·harmonyos
2501_920931703 小时前
React Native鸿蒙跨平台医疗健康类的血压记录,包括收缩压、舒张压、心率、日期、时间、备注和状态
javascript·react native·react.js·ecmascript·harmonyos
落霞的思绪4 小时前
配置React和React-dom为CDN引入
前端·react.js·前端框架
Hacker_Z&Q4 小时前
CSS 笔记2 (属性)
前端·css·笔记