React 18 开发环境下useEffect 会执行两次,原因分析及解决方案

这是React 18 开发环境下的经典现象:useEffect 在开发模式下会执行两次

1. 原因分析

React 18 的严格模式(Strict Mode)

在开发环境中,React 18 的严格模式故意在组件挂载时:

  1. 挂载组件

  2. 卸载组件

  3. 重新挂载组件

这样做的目的是帮助发现副作用问题(如缺少清理函数、不纯的副作用等)。

复制代码
// React 18 开发环境执行顺序
useEffect(() => {
  console.log('执行');     // 第一次执行
  return () => {
    console.log('清理');   // 立即清理
  };
}, []);
// 然后重新执行一次
// 所以会看到:执行 → 清理 → 执行

2. 解决方案

方案1:添加清理函数(推荐)

确保 useEffect 返回清理函数,避免重复请求造成的问题:

复制代码
useEffect(() => {
  let isMounted = true; // 防止组件已卸载后设置状态

  const fetchData = async () => {
    try {
      const response = await fetch('/api/data');
      const data = await response.json();
      
      if (isMounted) {
        setData(data);
      }
    } catch (error) {
      if (isMounted) {
        setError(error);
      }
    }
  };

  fetchData();

  return () => {
    isMounted = false; // 清理时标记为 false
  };
}, []);

方案2:使用请求取消机制

使用 AbortController 取消重复请求:

复制代码
useEffect(() => {
  const abortController = new AbortController();

  const fetchData = async () => {
    try {
      const response = await fetch('/api/data', {
        signal: abortController.signal
      });
      const data = await response.json();
      setData(data);
    } catch (error) {
      if (error.name !== 'AbortError') {
        setError(error);
      }
    }
  };

  fetchData();

  return () => {
    abortController.abort(); // 取消请求
  };
}, []);

方案3:使用 useRef 跳过第一次执行

复制代码
const isFirstRender = useRef(true);

useEffect(() => {
  if (isFirstRender.current) {
    isFirstRender.current = false;
    return;
  }
  
  // 第二次及以后才执行
  fetchData();
}, []);

方案4:使用 React Query / TanStack Query(推荐)

这是最优雅的方案,自动处理重复请求、缓存、重试等:

下载插件:

复制代码
npm install @tanstack/react-query

import { useQuery } from '@tanstack/react-query';

function MyComponent() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['myData'],
    queryFn: () => fetch('/api/data').then(res => res.json())
  });

  // 自动处理重复请求,开发环境也只发一次
}

六、总结

方案 优点 适用场景
清理函数 + isMounted 简单直接 小型项目,简单请求
AbortController 取消重复请求 需要取消请求的场景
React Query 功能完整,自动处理 中大型项目,数据密集型
相关推荐
刺客码1 小时前
Layui 表格固定列行高错位问题解决方案
前端框架·layui·jquery·web
Captaincc1 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
计算机魔术师3 小时前
德国Wiki被黑后两周,OpenAI终于把模型失控的账本摊开了
前端
kyriewen3 小时前
我让 AI 当面试官面了我一轮:第 3 个追问我就卡住了(附 10 道追问清单)
前端·面试·ai编程
IT_陈寒3 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端
前端snow4 小时前
ai agent --- 多agent框架之图编排引擎-langgraph
前端
竹林8184 小时前
OmniPic Studio v3.2.1 核心技术架构与全平台发版解析文档
前端·浏览器
JamesZhang800784 小时前
页面内存只涨不跌? 一次泄漏排查, 牵出 WeakMap 的诞生
前端
Z小明4 小时前
第 6 章 组件进阶
前端·vue.js
江华森4 小时前
HTTP请求的完整过程详解:从DNS解析到TCP挥手的微秒级实战分析
前端