在自定义Axios服务中使用React Hook报错的解决咨询
解决React Hook在服务函数中的调用错误
React Hook的规则明确要求:只能在React函数组件或自定义React Hook中调用Hook,所以直接在auth.service.tsx这类普通服务文件里调用useAxiosPrivate肯定会报错。下面给你三种可行的解决思路:
方案一:组件内调用Hook,把axios实例传给服务方法
既然Hook只能在组件里用,那就先在需要调用服务的组件中获取axiosPrivate实例,再把它作为参数传给服务方法。
修改auth.service.tsx:
// auth.service.tsx export const SharedService = { UpdateProfile: async (axiosPrivate, firstName:string, lastName:string) => { const response = await axiosPrivate.put('/user/me', { firstName, lastName, }); return response; } };
在组件中使用:
// 示例:ProfileEdit组件 import useAxiosPrivate from "../hooks/useAxiosPrivate"; import { SharedService } from "../services/auth.service"; const ProfileEdit = () => { const axiosPrivate = useAxiosPrivate(); const handleUpdate = async () => { try { await SharedService.UpdateProfile(axiosPrivate, "John", "Doe"); // 处理更新成功的逻辑 } catch (err) { // 处理错误 } }; return <button onClick={handleUpdate}>更新个人资料</button>; };
方案二:把服务封装成自定义Hook
直接把服务方法包装成一个自定义Hook,这样就能在内部合法调用其他Hook了。
新建useAuthService.tsx:
// useAuthService.tsx import useAxiosPrivate from "../hooks/useAxiosPrivate"; const useAuthService = () => { const axiosPrivate = useAxiosPrivate(); const UpdateProfile = async (firstName:string, lastName:string) => { const response = await axiosPrivate.put('/user/me', { firstName, lastName, }); return response; }; return { UpdateProfile }; }; export default useAuthService;
在组件中使用:
import useAuthService from "../hooks/useAuthService"; const ProfileEdit = () => { const { UpdateProfile } = useAuthService(); const handleUpdate = async () => { try { await UpdateProfile("John", "Doe"); // 处理成功逻辑 } catch (err) { // 处理错误 } }; return <button onClick={handleUpdate}>更新个人资料</button>; };
方案三:把拦截器逻辑移出Hook,整合到Axios实例
如果不想让服务依赖Hook,可以把token自动添加、刷新的逻辑从Hook里抽出来,直接绑定到axiosPrivate实例上,通过外部传入auth状态和refresh函数来初始化拦截器。
首先修改api/axios.ts:
// api/axios.ts import axios from 'axios'; export const axiosPrivate = axios.create({ baseURL: '/api', headers: { 'Content-Type': 'application/json' }, withCredentials: true }); // 定义初始化拦截器的方法 export const setupAxiosPrivateInterceptors = (auth, refresh) => { const requestIntercept = axiosPrivate.interceptors.request.use( (config) => { config.headers = config.headers ?? {}; if (!config.headers['Authorization']) { config.headers['Authorization'] = `Bearer ${auth?.accessToken}`; } return config; }, (error) => Promise.reject(error) ); const responseIntercept = axiosPrivate.interceptors.response.use( (response) => response, async (error) => { const prevRequest = error?.config; if ( (error?.response?.status === 403 || error?.response?.status === 401) && !prevRequest?.sent ) { prevRequest.sent = true; const newAccessToken = await refresh(); prevRequest.headers['Authorization'] = `Bearer ${newAccessToken}`; return axiosPrivate(prevRequest); } return Promise.reject(error); } ); return () => { axiosPrivate.interceptors.request.eject(requestIntercept); axiosPrivate.interceptors.response.eject(responseIntercept); }; };
然后修改原useAxiosPrivate.tsx(用来在组件中初始化拦截器):
// useAxiosPrivate.tsx import { axiosPrivate, setupAxiosPrivateInterceptors } from '../api/axios'; import { useEffect } from 'react'; import useRefreshToken from './useRefreshToken'; import useAuth from './useAuth'; const useAxiosPrivate = () => { const refresh = useRefreshToken(); const { auth }: any = useAuth(); useEffect(() => { const cleanup = setupAxiosPrivateInterceptors(auth, refresh); return cleanup; }, [auth, refresh]); return axiosPrivate; }; export default useAxiosPrivate;
最后修改auth.service.tsx,直接使用axiosPrivate:
// auth.service.tsx import { axiosPrivate } from "../api/axios"; export const SharedService = { UpdateProfile: async (firstName:string, lastName:string) => { const response = await axiosPrivate.put('/user/me', { firstName, lastName, }); return response; } };
注意:这种方式要确保调用服务方法前,已经有组件调用过useAxiosPrivate初始化拦截器,否则请求不会自动处理token。
内容的提问来源于stack exchange,提问作者Akshay
相关产品推荐
相关产品推荐

