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

带处理状态的React自定义Axios Hook引发无限循环问题求助

解决自定义Axios Hook引发的useEffect无限循环问题

问题根源

你的代码触发无限循环的核心原因有两个:

  1. 每次组件重渲染都会生成新的Axios实例:useAxios在组件每次重渲染(比如processing状态变化)时都会执行,调用axios.create()创建全新的实例,导致返回的axios引用每次都不一样。
  2. useEffect依赖不稳定:组件中useEffect的依赖数组包含axios,每次axios引用变化都会触发useEffect执行,调用API后又会修改processing状态,引发组件重渲染,形成循环。

移除axios依赖能暂时运行,但违反React Hooks的依赖规则,可能导致后续出现数据不一致等潜在问题。

解决方案

核心思路是缓存Axios实例,确保只有当配置参数变化时才重新创建实例,同时正确管理拦截器的生命周期避免内存泄漏。

优化后的自定义Hook代码

import { useState, useMemo, useEffect } from 'react';
import axios from 'axios';

const useAxios = ({
  baseURL = process.env.NEXT_PUBLIC_BACKEND_URL,
  withCredentials = true,
  headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json'
  }
}) => {
  const [processing, setProcessing] = useState(false);

  // 用useMemo缓存Axios实例,仅当配置参数变化时重新创建
  const instance = useMemo(() => {
    return axios.create({
      baseURL,
      headers,
      withCredentials,
    });
  }, [baseURL, headers, withCredentials]);

  // 绑定拦截器并处理清理逻辑
  useEffect(() => {
    // 请求拦截器:开始请求时设置processing为true
    const requestInterceptor = instance.interceptors.request.use(
      (config) => {
        setProcessing(true);
        return config;
      },
      (error) => {
        setProcessing(false);
        return Promise.reject(error);
      }
    );

    // 响应拦截器:请求结束(成功/失败)时设置processing为false
    const responseInterceptor = instance.interceptors.response.use(
      (response) => {
        setProcessing(false);
        return response;
      },
      (error) => {
        setProcessing(false);
        return Promise.reject(error);
      }
    );

    // 组件卸载或实例更新时,移除拦截器避免内存泄漏
    return () => {
      instance.interceptors.request.eject(requestInterceptor);
      instance.interceptors.response.eject(responseInterceptor);
    };
  }, [instance]);

  return {
    axios: instance,
    processing,
  };
};

export default useAxios;

关键优化点说明

  • useMemo缓存实例:将axios.create()包裹在useMemo中,依赖项设为传入的配置参数。只有当baseURL、headers或withCredentials变化时,才会生成新的Axios实例,确保组件重渲染时instance引用稳定。
  • useEffect管理拦截器:监听instance变化,动态添加/移除拦截器,避免重复绑定拦截器导致的逻辑混乱,同时通过清理函数防止内存泄漏。
  • 稳定的依赖项:组件中useEffect依赖的axios现在是稳定引用,不会因processing状态变化而触发重复执行。

使用示例

import { useEffect, useState } from 'react';
import useAxios from './useAxios';

const DataComponent = () => {
  const { axios, processing } = useAxios();
  const [data, setData] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const res = await axios.get('/api/some-endpoint');
        setData(res.data);
      } catch (err) {
        console.error('请求失败:', err);
      }
    };

    fetchData();
  }, [axios]); // 依赖稳定,不会引发无限循环

  return (
    <div>
      {processing ? (
        <div>加载中...</div>
      ) : data ? (
        <div>获取的数据:{JSON.stringify(data)}</div>
      ) : (
        <div>暂无数据</div>
      )}
    </div>
  );
};

export default DataComponent;

扩展建议

如果需要支持多个并发请求的加载状态,可以将processing改为计数器:

  • 请求拦截器中setProcessing(prev => prev + 1)
  • 响应拦截器中setProcessing(prev => prev - 1)
  • 显示加载状态时判断processing > 0,这样能准确反映所有请求的处理状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 02:10:51