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

React中发起AJAX请求时出现无限循环问题求助

解决React中重复发起API请求的问题

我刚学React的时候也踩过一模一样的坑!你说的情况其实很常见,主要是对React的渲染机制和副作用处理还不太熟悉,咱们一步步来捋清楚:

为什么会重复发起请求?

首先,React组件并不是只渲染一次——哪怕你没主动改状态,以下情况都可能触发组件重新渲染:

  • 开发环境下的Strict Mode:React会故意重复调用组件函数、useEffect回调等,用来检测潜在的副作用问题,这会让你的请求多执行几次
  • 父组件重新渲染:如果你的电影列表组件是某个父组件的子组件,父组件的状态或props变化时,子组件也会跟着重新渲染
  • 你把API请求写在了组件的渲染阶段(比如函数组件的顶层、JSX的表达式里):每次组件渲染都会执行这段代码,自然会重复发起请求

你以为只有状态变化才会触发请求,但实际上只要组件重新渲染,渲染阶段的代码就会重新跑一遍,这就是问题所在。

怎么解决?

核心思路是把API请求放在副作用钩子useEffect里,通过依赖数组控制请求的执行时机:

1. 基础写法:只在组件挂载时请求一次

如果你的电影列表只需要加载一次,就给useEffect传一个空的依赖数组[],这样它只会在组件第一次挂载时执行:

import { useState, useEffect } from 'react';

function MovieList() {
  const [movies, setMovies] = useState([]);

  useEffect(() => {
    // 封装请求逻辑
    const fetchMovies = async () => {
      try {
        const response = await fetch('你的API地址');
        const movieData = await response.json();
        setMovies(movieData);
      } catch (error) {
        console.error('加载电影失败:', error);
      }
    };

    fetchMovies();
  }, []); // 空依赖数组 = 仅执行一次

  return (
    <div className="movie-list">
      {movies.map(movie => (
        <div key={movie.id} className="movie-item">
          <h3>{movie.title}</h3>
          <img src={movie.poster} alt={`${movie.title}的海报`} />
        </div>
      ))}
    </div>
  );
}

2. 进阶:处理依赖变化时重新请求

如果你的请求需要根据某个变量(比如用户选择的分类)动态调整,就把这个变量放进依赖数组,只有当它变化时才重新发起请求:

// 比如根据分类筛选电影
useEffect(() => {
  const fetchMoviesByCategory = async () => {
    const response = await fetch(`你的API地址?category=${selectedCategory}`);
    const movieData = await response.json();
    setMovies(movieData);
  };

  fetchMoviesByCategory();
}, [selectedCategory]); // 当selectedCategory变化时,重新请求

3. 额外优化:取消不必要的请求

为了避免组件卸载或者重复请求时的内存泄漏,可以用AbortController来取消未完成的请求:

useEffect(() => {
  const controller = new AbortController();
  const signal = controller.signal;

  const fetchMovies = async () => {
    try {
      const response = await fetch('你的API地址', { signal });
      const movieData = await response.json();
      setMovies(movieData);
    } catch (error) {
      // 忽略取消请求的错误
      if (error.name !== 'AbortError') {
        console.error('加载电影失败:', error);
      }
    }
  };

  fetchMovies();

  // 组件卸载或重新渲染时,取消请求
  return () => controller.abort();
}, []);

最后再提醒几个坑

  • 不要把请求写在组件顶层或者JSX里:比如直接在const [movies, setMovies] = useState([]);下面写fetch,这会每次渲染都执行
  • 依赖数组不要乱写:如果依赖了一个每次渲染都会变化的对象/函数,会导致useEffect重复执行,这时候可以用useCallback或useMemo缓存一下
  • 开发环境的Strict Mode:如果你在开发模式下看到请求执行两次,别慌,这是React的正常检测机制,生产环境下不会出现

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 08:15:46