As you know, you can not use React hooks in a place other than React component or custom hooks.
In this article, you will learn how to use hooks in the Axios interceptors.
First, create a React project.
yarn create react-app hooks-in-axios-interceptors
// or
npx create-react-app hooks-in-axios-interceptors
Then create an Axios instance with custom configuration.
import axios from 'axios';
const instance = axios.create({
baseURL: "https://example.com"
})
export default instance;
As we need React component to use the hooks, let's write a component here.
import axios from 'axios';
let instance = axios.create({
baseURL: "https://example.com"
})
function AxiosInterceptor({children}) {
return children;
}
export default instance;
export {AxiosInterceptor}
Adding an interceptor in a component is a side effect, so we get help from useEffect
hook.
Add the interceptors to the Axios instance in the useEffect
.
Note : you must remove interceptors in useEffect
return statement, because every execution of useEffect
, adds a new interceptor to Axios instance.
import axios from 'axios';
import { useEffect } from 'react'
const instance = axios.create({
baseURL: "https://example.com"
})
const AxiosInterceptor = ({ children }) => {
useEffect(() => {
const resInterceptor = response => {
return response;
}
const errInterceptor = error => {
return Promise.reject(error);
}
const interceptor = instance.interceptors.response.use(resInterceptor, errInterceptor);
return () => instance.interceptors.response.eject(interceptor);
}, [])
return children;
}
export default instance;
export { AxiosInterceptor }
Now, you can import the intended hooks and use them in the interceptors handler.
for instance, if you want to redirect the request which come back with the status code 401, to the login page, you may use the useNavigate
hook ( from react-router-dom ) or use the useRouter
hook ( from next/router ).
import axios from 'axios';
import { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
const instance = axios.create({
baseURL: "https://example.com"
})
const AxiosInterceptor = ({ children }) => {
const navigate = useNavigate();
useEffect(() => {
const resInterceptor = response => {
return response;
}
const errInterceptor = error => {
if (error.response.status === 401) {
navigate('/login');
}
return Promise.reject(error);
}
const interceptor = instance.interceptors.response.use(resInterceptor, errInterceptor);
return () => instance.interceptors.response.eject(interceptor);
}, [navigate])
return children;
}
export default instance;
export { AxiosInterceptor }
Note : as we do not want to destroy the SPA behavior of our application, we don't use window.location
for redirecting.
Finally, we wrap the app with the Axios interceptor component.
import { AxiosInterceptor } from './axios'
import ThemeProvider from './theme-context'
import AuthProvider from './auth-context'
function App() {
return (
<ThemeProvider>
<AuthProvider>
<AxiosInterceptor>
<div>
App
</div>
</AxiosInterceptor >
</AuthProvider>
</ThemeProvider>
);
}
export default App;
Note : if you want to access the other context data, the Axios interceptor component must be the child of the context providers.
Codesandbox Example
conclusion
In this way, you can use the React hooks in the Axios interceptors easily.
Top comments (19)
useEffect is asynchronous to children's effect, so request call might be at the same time with interceptor setup and even sooner, in that case the interceptor might not work as we expected.
So the solution is set a state as
false
initially, and set it totrue
after interceptor setup complete, then we return thechildren
Damn you save my life, I'd been searching this issue for a full day. You are my hero
This is my code. In the request interceptor is not call all the time. After login, useEffect of the next screen it makes an API call in that call it should set header from redux. But it is not working. Where is the mistake I make?
i think a better approach is to prevent the request from running on initial render. this helped me, stackoverflow
@iamyoki 's answer is workin for me!! And rather simpler to understand.
I indeed tried preventing the request from running on initial render as you suggested but the interceptor stayed unsed for the following renders too.
You are my hero, Thank you very much
yes, i got the same issue and tried your solution to return isSet && children, however typescirpt does not allow to do that.
How can I fix in that case?
@mohaymenrafi
try to wrap children in empty tag (i.e: or <></>) and it will work
Thanks for this post! what does "Adding an interceptor in a component is a side effect, so we get help from useEffect hook." mean? i followed this post just same. but in my case there is some problem. so i deleted 'return () => instance.interceptors.response.eject(interceptor);' <- this line.
it's works but little weird.. it runs always twice because of and first is always fail, only second one works. once i delete the then it doesn't work.. so! i deleted useEffect() then it works very well. i tried that using only without useEffect(). then interceptor runs 4 times............i don't know why, but i wanna know...
Thanks for the comment.
Adding an interceptor in a component is a side effect because we are manipulating something that is out of our scope of component.
you should eject previous interceptor in the clean up function and in the case you didn't, each time the useEffect is executed, one interceptor will be added to interceptors stack and this not the expected behaviour.
And be aware of new behaviour of strict mode in React 18 that execute effect twice in development mode. you can read more about that in this link Adding Reusable State to StrictMode
I created a codesandbox for the post react 18 codesandbox and a react 17 codesandbox
you can find out that the effects run twice in React 18.
Thanks to your explanation, I totally understand! Your writing saved me, and the comments helped me a lot!
I put eject code back because I thought I needed it and It works well! and also your explanation about strict mode in React 18 helped me! it much better! Thank you! 😊
Thank you ☺️
Great post. Thanks for sharing😍
Thanks, this really helped me!
I inspired for this post and created a stand-alone hook for the interceptors here.
I had a lot of providers and I wanted to avoid that.
Great post. It really helped me a lot.
One question here, why we are passing children here or what is the reason for making the Interceptor component as a provider?
Answering the question would clear my concept more on interceptors.
Thanks again.
I didn't know how to use react to make a unified error interception jump for api before seeing your post, thank you so much, it's literally helpful ! (๑•̀ㅂ•́)و✧
Thank you very much!
Thanks for sharing!
In my case, using with useEffect does not work. Maybe I could use with a state to control if the useEffect finished it, but I really don't understand why I need useEffect
Thanks for this post. I also need to write UTC for it. Can you please assist