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

React自定义useAxios钩子多API调用优化方案咨询

问题解答

当前你的useAxios钩子仅能维护单个请求的响应、错误和加载状态,因此多API调用时确实需要重复声明多次const [data1, error1, loading1, axiosFetch] = useAxios(),这会导致代码冗余。要精简代码,需要修改钩子使其支持同时管理多个请求的状态,以下是具体实现方案:


修改后的useAxios钩子

将状态改为对象结构,用请求唯一标识(key)区分不同请求的状态,同时维护多个请求的取消控制器:

import { useState, useEffect } from 'react';

const useAxios = (defaultAxiosInstance) => {
  // 存储所有请求的响应:{ [key]: data }
  const [responses, setResponses] = useState({});
  // 存储所有请求的错误:{ [key]: errorMsg }
  const [errors, setErrors] = useState({});
  // 存储所有请求的加载状态:{ [key]: boolean }
  const [loadings, setLoadings] = useState({});
  // 存储所有请求的取消控制器:{ [key]: AbortController }
  const [controllerMap, setControllerMap] = useState({});

  const axiosFetch = async (configObj) => {
    const { 
      key, 
      axiosInstance = defaultAxiosInstance, 
      method, 
      url, 
      requestConfig = {} 
    } = configObj;

    if (!key) throw new Error('必须传入key作为请求唯一标识');

    try {
      // 取消当前key的上一次未完成请求
      if (controllerMap[key]) controllerMap[key].abort();
      
      const ctrl = new AbortController();
      setControllerMap(prev => ({ ...prev, [key]: ctrl }));
      // 标记当前请求为加载中
      setLoadings(prev => ({ ...prev, [key]: true }));
      // 清空当前请求的历史错误
      setErrors(prev => ({ ...prev, [key]: '' }));

      const res = await axiosInstance[method.toLowerCase()](url, {
        ...requestConfig,
        signal: ctrl.signal // 绑定取消信号
      });

      setResponses(prev => ({ ...prev, [key]: res.data }));
    } catch (err) {
      // 忽略主动取消请求的错误
      if (err.name !== 'AbortError') {
        console.error(err.message);
        setErrors(prev => ({ ...prev, [key]: err.message }));
      }
    } finally {
      setLoadings(prev => ({ ...prev, [key]: false }));
    }
  };

  useEffect(() => {
    // 组件卸载时取消所有未完成的请求
    return () => Object.values(controllerMap).forEach(ctrl => ctrl.abort());
  }, [controllerMap]);

  // 便捷获取单个请求的状态
  const getRequestState = (key) => ({
    data: responses[key],
    error: errors[key],
    loading: loadings[key] ?? false
  });

  return { axiosFetch, getRequestState, responses, errors, loadings };
};

export default useAxios;

组件中使用示例

只需调用一次useAxios,即可管理任意多个请求:

import useAxios from './useAxios';
import axios from './your-axios-instance'; // 你的全局axios实例(带BASE_URL和headers)

const MyComponent = () => {
  // 传入默认axios实例,后续调用无需重复传
  const { axiosFetch, getRequestState } = useAxios(axios);

  // 获取两个请求的状态
  const { data: urlData, error: urlError, loading: urlLoading } = getRequestState('fetchUrl');
  const { data: usersData, error: usersError, loading: usersLoading } = getRequestState('fetchUsers');

  // 初始化加载第一个接口
  useEffect(() => {
    axiosFetch({
      key: 'fetchUrl',
      method: 'GET',
      url: '/url'
    });
  }, [axiosFetch]);

  // 按钮触发加载用户接口
  const handleGetUsers = () => {
    axiosFetch({
      key: 'fetchUsers',
      method: 'GET',
      url: '/users'
    });
  };

  return (
    <div>
      {urlLoading && <p>加载基础数据中...</p>}
      {urlError && <p>基础数据加载失败:{urlError}</p>}
      {urlData && <pre>{JSON.stringify(urlData, null, 2)}</pre>}

      <button onClick={handleGetUsers} disabled={usersLoading}>
        {usersLoading ? '加载用户中...' : '获取用户列表'}
      </button>
      {usersError && <p>用户数据加载失败:{usersError}</p>}
      {usersData && <pre>{JSON.stringify(usersData, null, 2)}</pre>}
    </div>
  );
};

export default MyComponent;

核心优势

  1. 仅需调用一次useAxios即可管理N个请求的状态,避免代码冗余
  2. 每个请求通过唯一key区分,状态互不干扰
  3. 自动取消同key的上一次未完成请求,防止重复请求导致的状态混乱
  4. 组件卸载时自动取消所有未完成请求,避免内存泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 12:35:20