Files
MattrixwvReactComponents/lib/provider/axios/AxiosProvider.tsx

94 lines
2.0 KiB
TypeScript

import type { AxiosError, AxiosInstance } from "axios";
import axios from "axios";
import { createContext, useContext, useMemo } from "react";
import { useToken } from "../token";
export interface AxiosState {
publicApi: AxiosInstance;
authorizedApi: AxiosInstance;
}
const initialState: AxiosState = {
publicApi: {} as AxiosInstance,
authorizedApi: {} as AxiosInstance
}
const AxiosContext = createContext<AxiosState>(initialState);
export default function AxiosProvider({
apiUrl,
children
}: Readonly<{
apiUrl: string;
children: React.ReactNode;
}>){
const { getToken } = useToken();
const publicApi = useMemo(() => {
const api = axios.create({
baseURL: apiUrl
});
return api;
}, [apiUrl]);
const authorizedApi = useMemo(() => {
const api = axios.create({
baseURL: apiUrl
});
api.interceptors.request.use(async (config) => {
try{
const token = await getToken();
if(token){
config.headers.Authorization = `Bearer ${token}`;
}
return config;
}
catch(error){
return Promise.reject(error as Error);
}
});
api.interceptors.response.use(r => r, async (error: AxiosError) => {
const original = error.config;
if(!original){
return Promise.reject(error);
}
if(error.response?.status === 401 && !original._retry){
original._retry = true;
try{
const newToken = await getToken();
original.headers.Authorization = `Bearer ${newToken}`;
return api(original);
}
catch(refreshError){
return Promise.reject(refreshError as Error);
}
}
});
return api;
}, [apiUrl, getToken]);
const value = useMemo(() => ({
publicApi,
authorizedApi
}), [authorizedApi, publicApi]);
return (
<AxiosContext.Provider value={value}>
{children}
</AxiosContext.Provider>
);
}
// eslint-disable-next-line react-refresh/only-export-components
export function useAxios(){
const context = useContext(AxiosContext);
if(!context){
throw new Error("useAxios must be called inside an AxiosProvider");
}
return context;
}