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)