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. 理解依赖项行为

    • 确保依赖项的正确性,避免遗漏或过多依赖项,导致副作用重复执行。
相关推荐
永日456704 分钟前
学习日记-HTML-day51-9.9
前端·学习·html
狗头大军之江苏分军20 分钟前
iPhone 17 vs iPhone 17 Pro:到底差在哪?买前别被忽悠了
前端
小林coding21 分钟前
再也不怕面试了!程序员 AI 面试练习神器终于上线了
前端·后端·面试
文心快码BaiduComate33 分钟前
WAVE SUMMIT深度学习开发者大会2025举行 文心大模型X1.1发布
前端·后端·程序员
babytiger33 分钟前
python 通过selenium调用chrome浏览器
前端·chrome
passer98140 分钟前
基于webpack的场景解决
前端·webpack
奶昔不会射手1 小时前
css3之grid布局
前端·css·css3
举个栗子dhy1 小时前
解决在父元素上同时使用 onMouseEnter和 onMouseLeave时导致下拉菜单无法正常展开或者提前收起问题
前端·javascript·react.js