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;
核心优势
- 仅需调用一次
useAxios即可管理N个请求的状态,避免代码冗余 - 每个请求通过唯一
key区分,状态互不干扰 - 自动取消同
key的上一次未完成请求,防止重复请求导致的状态混乱 - 组件卸载时自动取消所有未完成请求,避免内存泄漏
内容的提问来源于stack exchange,提问作者manish thakur
相关产品推荐
相关产品推荐

