英文:
how to handle multiple axios instances
问题
我创建了一个名为useAxios的钩子,在这个钩子中,我从状态中获取我的令牌,然后使用它来创建Axios的实例:
但现在我担心在多个组件中使用这个钩子会创建多个Axios实例!所以我的问题是:
1) 我如何检查是否仍然只创建了一个实例,每次我只是覆盖它,还是我实际上创建了多个实例?
2) 如果是多个实例,是否没问题?
3) 我可以使用useEffect或useCallback来仅在令牌更改时创建它,但是否有其他常见的解决方案?
英文:
I made a hook called useAxios where I get my token from the state and then use it to create an instance of Axios:
export const useAxios = () => {
const { userData } = useUser();
const customAxios = axios.create({
headers: { Authorization: `Bearer ${userData?.user?.token}` },
});
return customAxios;
};
But now I'm worried that by using this hook in multiple components I'm creating many instances of Axios! so my questions are:
-
how can I check if there is still one instance created and every time I'm just overwriting it OR I'm actually creating multiple instances?
-
in case it's multiple instances, is it fine?
-
I can use useEffect or useCallback to make it created only when the token is changed but is there any other common solution for that?
答案1
得分: 0
首先,在任何组件中使用此钩子时,axios实例将在每次调用时创建,而不会覆盖先前的实例,因为根据您的实现,它应该创建一个实例并返回它,所以它根本没有意义,它会覆盖之前的实例,因为实际上之前并没有实例!
那么,如何解决这个问题?
基本上不要在钩子内部创建实例,而是在钩子外部创建实例,并在钩子内部访问它,然后可以对它执行任何操作。这样可以使钩子仅操纵实例而不创建多个实例。
如何知道是否创建了多个实例?
只要您遵循这种方法,肯定会创建多个实例。事实上,我不知道特定的方法来知道是否创建了多个实例,或者也许我当前没有考虑到这一点,但对我来说,这种方法会明显创建多个实例。
如果有多个实例,是否可以接受?
这个问题没有明确的答案,只要它不违反您的逻辑(思想)和性能,那就可以接受。但我认为这会使您的代码变得不受控制,或者创建具有不同配置的多个实例并不好,因为这可能会让您感到困惑(我不确定,我只是在思考)。
我可以使用useEffect或useCallback仅在令牌更改时创建它,但还有其他常见的解决方案吗?
您的问题中有一些问题,即“创建”,我不太明白,您是否希望在令牌更改时每次重新创建它?
好吧,也许这不是最佳选择,因为axios提供了一些内部方法,允许您在标头中更改令牌,这些方法称为拦截器。它们与中间件在方法上非常相似(如果您熟悉中间件),它们的工作方式如下:
- axios接收您的请求
- 然后axios将其传递给拦截器
- 拦截器可以执行任何操作,但应在逻辑结束时返回请求
这使您能够验证令牌是否有效,如果无效,您可以向服务器发送请求以获取新令牌,然后将其附加到标头,然后继续处理请求。这是我在当前项目中使用的代码,用于验证每个请求的令牌:
let accessToken = localStorage.getItem("access")
? JSON.parse(localStorage.getItem("access") ?? "")
: "";
const refreshToken = localStorage.getItem("refresh")
? JSON.parse(localStorage.getItem("refresh") ?? "")
: "";
axiosInstance.interceptors.request.use(async (req) => {
if (!accessToken) {
accessToken = localStorage.getItem("access")
? JSON.parse(localStorage.getItem("access") ?? "")
: null;
req.headers!.Authorization = `Bearer ${accessToken}`;
}
const user: any = jwt_decode(accessToken);
const isExpired = dayjs.unix(user.exp).diff(dayjs()) < 1;
if (!isExpired) return req;
const response = await axios.post(`${API_BASE_URL}/auth/refresh/`, {
refresh: refreshToken,
});
localStorage.setItem("access", JSON.stringify(response.data.access));
localStorage.setItem("refresh", JSON.stringify(response.data.refresh));
req.headers!.Authorization = `Bearer ${JSON.parse(response.data.access)}`;
return req;
});
当然,我使用了一些外部库,代码相当复杂,但如果您进行研究,就会发现这比使用useEffect要容易得多,也更好。
那么我不能使用useEffect
和useCallback
来实现吗?
当然,您可以这样做,但在我看来,这不是一个很好的解决方案(仅代表我的个人观点)。
如果您认为我的回答不够好或不够清晰,请留下评论,我们可以讨论您的想法。
英文:
Firstly, when you use this hook in any component the axios instance will be created each time you call it not overwriting a previous one due to your implementation it should create an instance and return it, so it makes absolutely no sense that it'll override some previous one because actually there're no previous ones!
So, how to solve this?
basically don't create the instance inside the hook, access it instead.
create the instance outside the hook and access it within the hook and do anything you want with it.
this will make the hook just manipulate the instance without creating a one
How to know if I'm creating multiple instances?
as long as you're following this approach you're definitely creating multiple instances, in fact, I don't know a particular way to know if I'm creating multiple instances or maybe it's not present in my mind currently, but for me, it's obvious that this approach will create multiple instances.
In case it's multiple instances, is it fine?
there's no proper answer to this question, as long as it's not going against your logic (idea) or/and perf so it's fine but I'm thinking that this will make your code quite uncontrolled or make multiple instances with different configurations isn't good because this would make you lost (I'm not sure, I'm just thinking.)
I can use useEffect or useCallback to make it created only when the token is changed but is there any other common solution for that?
there's something wrong with your question, which is "created", I don't understand do you want to recreate it every time the token changes?
okay maybe it's not the best option because axios provides some internal methodology that allows you to change the token in the header, which are interceptors
they're very similar to Middleware in the methodology (if you're familiar with Middleware) they work like the following:
- axios receives your request
- then axios will pass it to the interceptor
- the interceptor can do anything but should return the request at the end of the logic
this gives you the ability to verify that the token is valid and if it's not you'll make a request to the server to get a new one then append it to the headers then proceed with the request, this is a code of mine that I'm using it in my current project to verify the token on each request
<!-- begin snippet: js hide false console: true babel: false -->
<!-- language: lang-js -->
let accessToken = localStorage.getItem("access")
? JSON.parse(localStorage.getItem("access") ?? "")
: "";
const refreshToken = localStorage.getItem("refresh")
? JSON.parse(localStorage.getItem("refresh") ?? "")
: "";
axiosInstance.interceptors.request.use(async (req) => {
if (!accessToken) {
accessToken = localStorage.getItem("access")
? JSON.parse(localStorage.getItem("access") ?? "")
: null;
req.headers!.Authorization = `Bearer ${accessToken}`;
}
const user: any = jwt_decode(accessToken);
const isExpired = dayjs.unix(user.exp).diff(dayjs()) < 1;
if (!isExpired) return req;
const response = await axios.post(`${API_BASE_URL}/auth/refresh/`, {
refresh: refreshToken,
});
localStorage.setItem("access", JSON.stringify(response.data.access));
localStorage.setItem("refresh", JSON.stringify(response.data.refresh));
req.headers!.Authorization = `Bearer ${JSON.parse(response.data.access)}`;
return req;
});
<!-- end snippet -->
of course, I've used some external libraries and the code is quite complex but if you do research about it you'll see that it's a much easier and better option than using useEffect.
Then I can't use useEffect
and useCallback
to do so?
definitely, you can do that but I'm not seeing this good solution (in my personal opinion)
if you're seeing my answer wasn't good or/and clear enough please make a comment and let's discuss what you're thinking about.
答案2
得分: 0
感谢@Youssef_Ayman168的建议,我找到了这个解决方案:
axios.interceptors.request.use(
(config) => {
const token = JSON.parse(localStorage.getItem(USER))?.token;
if (token) {
config.headers["Authorization"] = "Bearer " + token;
}
// config.headers['Content-Type'] = 'application/json';
return config;
},
(error) => {
console.log({ error });
Promise.reject(error);
}
);
export { axios };
然后,当需要时,我只需导入这个 Axios 实例。
英文:
thanks to @Youssef_Ayman168 suggestion I find this solution:
axios.interceptors.request.use(
(config) => {
const token = JSON.parse(localStorage.getItem(USER))?.token;
if (token) {
config.headers["Authorization"] = "Bearer " + token;
}
// config.headers['Content-Type'] = 'application/json';
return config;
},
(error) => {
console.log({ error });
Promise.reject(error);
}
);
export { axios };
and then I just import this instance of Axios when I need it.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论