How to use router inside axios interceptors. React and Vue
Tags: router, react, vue, axiosinterceptor
Published: 2023-03-29 18:05:02

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.

```ts
import { NavigateFunction } from "react-router-dom";

const globalRouter = { navigate: null } as {
  navigate: null | NavigateFunction;
};

export default globalRouter;
```

- 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.

```tsx
import { Route, Routes, useNavigate } from "react-router-dom";
import globalRouter from "./globalRouter";

function App() {
  const navigate = useNavigate();
  globalRouter.navigate = navigate;
}

export default App;
```

- And finally, we use the router inside of axios interceptors

```ts
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;

```
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.

<img src="https://i.imgur.com/v9fPt.gif" />


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](https://github.com/vuejs/router/issues/1601).



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


```ts
import { Router } from "vue-router";

const globalRouter = { router: null } as { router: null | Router };

export { globalRouter };
```

- 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

```ts
<script setup lang="ts">

import { useRouter } from "vue-router";
import { globalRouter } from "./router/globalRouter";

const router = useRouter();
globalRouter.router = router;

</script>

``` 

- Finally we can import router from `globalRouter.ts` file in our axios instance definition and use it in interceptors

```ts
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;
```
both hot reloading and navigation are now functioning properly.