You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中如何使用useEffect避免不必要的API重复调用?

React useEffect空依赖数组下API重复调用问题

问题描述

React初学者在实现列表渲染功能时遇到异常:为useEffect传入空依赖数组,预期相关请求逻辑仅在组件挂载时执行一次,但实际运行中API始终处于被重复调用的状态,无法定位问题产生的原因。

问题复现代码

import React from "react";
import axios from "axios";
import "./styles.css";

const App = () => {
  const BASE_URL = "https://pokeapi.co/api/v2";

  const [pokemons, setPokemons] = React.useState([]);

  const getAllPokemons = async () => {
    const { data } = await axios.get(`${BASE_URL}/pokemon`);

    data.results.map((pokemon) => getPokeType(pokemon));
  };

  const getPokeType = async (pokemon) => {
    const { data } = await axios.get(pokemon.url);

    setPokemons((prev) => [...prev, data]);
  };

  React.useEffect(() => {
    getAllPokemons();
  }, []);

  console.log(pokemons);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      {pokemons.map((pokemon) => (
        <p key={pokemon.id} style={{ color: "blue" }}>
          {pokemon.name}
        </p>
      ))}
    </div>
  );
};

export default App;

问题根因

重复调用由两个因素共同导致:

  • React 18 严格模式默认行为:开发环境下严格模式会自动执行「组件挂载→卸载→重新挂载」流程,用于提前检测未写清理逻辑的副作用,即使useEffect传入空依赖数组,开发环境下副作用逻辑也会执行2次,这是框架的设计行为,不是依赖配置错误。
  • 频繁状态更新触发多次重渲染:现有代码中每获取到1个宝可梦的详情数据就调用一次setPokemons,接口默认返回20条宝可梦数据,就会触发20次组件重渲染,控制台多次打印日志会进一步放大“接口无限重复调用”的观感。

修复方案

  1. 为useEffect添加清理逻辑,组件卸载时取消未完成的请求,避免严格模式下二次挂载触发重复请求、以及组件卸载后状态更新报错的问题。
  2. 优化状态更新逻辑:等待所有宝可梦详情请求全部返回后,一次性更新pokemons状态,减少不必要的重渲染。
    修复后核心代码示例:
React.useEffect(() => {
  const controller = new AbortController();
  const signal = controller.signal;

  const getAllPokemons = async () => {
    try {
      const { data } = await axios.get(`${BASE_URL}/pokemon`, { signal });
      // 等待所有详情请求并行完成
      const pokeDetailList = await Promise.all(
        data.results.map(async (pokemon) => {
          const res = await axios.get(pokemon.url, { signal });
          return res.data;
        })
      );
      setPokemons(pokeDetailList);
    } catch (err) {
      // 主动取消的请求不需要抛出错误
      if (axios.isCancel(err)) return;
      console.error(err);
    }
  };

  getAllPokemons();
  // 组件卸载时终止所有未完成的请求
  return () => controller.abort();
}, []);

补充说明:如果不需要严格模式的开发检测能力,可以直接移除入口文件中包裹根组件的<React.StrictMode>标签,但更推荐按照上述方案补全副作用清理逻辑,从根源上避免线上环境出现内存泄漏、已卸载组件状态更新报错等问题。


内容的提问来源于stack exchange,提问作者Katyellen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 06:24:30