DEV Community

David Buchukuri
David Buchukuri

Posted on

How to use router inside axios interceptors. React and Vue

While building a frontend application using React and Vue, I encountered an issue that required me to use Axios interceptors within a custom Axios instance to handle 401 responses by redirecting users to the login page. However, I encountered errors when attempting to use the useNavigate hook in React or the useRouter hook in Vue. The errors indicated that I could not use the router outside of React or Vue components.

React

After some research, I found a solution to the problem in an article by Arian Hamdi, which can be found at https://dev.to/arianhamdi/react-hooks-in-axios-interceptors-3e1h.
However, while this solution works, I didn't find it to be the most elegant one, and I noticed potential bugs mentioned in the comments under the article. Therefore, I continued to research and eventually developed my own solution to the issue.
The approach I found to handle the issue is a simple and straightforward one. Since we can't use hooks outside of the components, we create a router inside the component. Then we save the router in a separate file, which can be imported wherever it is needed.

  • We first need to create a globalRouter.ts file in the src directory. This file will contain our router.
 import { NavigateFunction } from "react-router-dom"; const globalRouter = { navigate: null } as { navigate: null | NavigateFunction; }; export default globalRouter; 
Enter fullscreen mode Exit fullscreen mode
  • Next, we go to the topmost component of our application (in this case, App.tsx) and add the code to save the router in our file.
 import { Route, Routes, useNavigate } from "react-router-dom"; import globalRouter from "./globalRouter"; function App() { const navigate = useNavigate(); globalRouter.navigate = navigate; } export default App; 
Enter fullscreen mode Exit fullscreen mode
  • And finally, we use the router inside of axios interceptors
 import globalRouter from "../globalRouter"; import axios from "axios"; const customAxios = axios.create(); customAxios.interceptors.response.use( function (response) { return response; }, function (error) { if (error.response.status == 401 && globalRouter.navigate) { globalRouter.navigate("/login"); } return Promise.reject(error); } ); export default customAxios; 
Enter fullscreen mode Exit fullscreen mode

Note that we added an additional check in the if statement for globalRouter.navigate. We declared its type as a union, which includes null. Although we set the router's value in the topmost component, TypeScript forces us to make that check to ensure type safety.

Vue

Although useRouter is a convenient hook in Vue.js, it can only be used within Vue components. If you're using axios in your Vue.js application, you may have come across a solution where you import the router instance into your axios definition and call methods on it directly. While this may seem like a viable solution, it can lead to a significant problem with hot reloading.

I discovered this problem when I noticed that hot reloading wasn't working for the components where I was using my custom axios instance.

After some investigation, I realized that the direct usage of the router instance was causing the issue.

Whenever I made changes to my component, hot reloading didn't occur, and an error appeared in the console: [hmr] Failed to reload /src/[Component.vue]. This could be due to syntax errors or importing non-existent modules. (see errors above).. To learn more about this error, you can refer to the GitHub issue here.

To get around this issue, we can take a similar approach to the one mentioned earlier. We can create a router using the hook inside a component and store it in a separate file, which can be imported wherever needed.

To implement this solution

  • We'll first create a globalRouter.ts file that will store our router
 import { Router } from "vue-router"; const globalRouter = { router: null } as { router: null | Router }; export { globalRouter }; 
Enter fullscreen mode Exit fullscreen mode
  • Then we'll create a router in the topmost component (in this case App.vue) and store it in the object inside globalRouter.ts file
 <script setup lang="ts"> import { useRouter } from "vue-router"; import { globalRouter } from "./router/globalRouter"; const router = useRouter(); globalRouter.router = router; </script>  
Enter fullscreen mode Exit fullscreen mode
  • Finally we can import router from globalRouter.ts file in our axios instance definition and use it in interceptors
 import axios from "axios"; import { globalRouter } from "../router/globalRouter"; const axiosInstance = axios.create(); axiosInstance.interceptors.response.use( function (response) { return response; }, function (error) { if (error.response.status == 401) { globalRouter.router?.push("/"); } return Promise.reject(error); } ); export default axiosInstance; 
Enter fullscreen mode Exit fullscreen mode

both hot reloading and navigation are now functioning properly.

Top comments (0)