Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React router v6 how to use `navigate` redirection in axios interceptor

import axios from "axios";
import { useNavigate } from "react-router-dom";

export const api = axios.create({
  baseURL: "http://127.0.0.1:8000/",
  headers: {
    "content-type": "application/json",
  },
});

api.interceptors.response.use(
  function (response) {
    return response;
  },
  function (er) {
    if (axios.isAxiosError(er)) {
      if (er.response) {
        if (er.response.status == 401) {

          // Won't work
          useNavigate()("/login");

        }
      }
    }

    return Promise.reject(er);
  }
);
like image 478
januw a Avatar asked Sep 05 '25 03:09

januw a


2 Answers

In the pre-RRDv6 world you would create a custom history object, to be exported and imported and passed to a Router, and imported and accessible in external javascript logic, like redux-thunks, axios utilities, etc.

To replicate this in RRDv6 you need to also create a custom router component so it can be passed an external history object. This is because all the higher level RRDv6 routers maintain their own internal history contexts, so we need to duplicate the history instantiation and state part and pass in the props to fit the base Router component's new API.

import { Router } from "react-router-dom";

const CustomRouter = ({ history, ...props }) => {
  const [state, setState] = useState({
    action: history.action,
    location: history.location
  });

  useLayoutEffect(() => history.listen(setState), [history]);

  return (
    <Router
      {...props}
      location={state.location}
      navigationType={state.action}
      navigator={history}
    />
  );
};

Create the history object you need:

import { createBrowserHistory } from "history";

const history = createBrowserHistory();

export default history;

Import and pass to the new CustomRouter:

import customHistory from '../path/to/history';

...

<CustomRouter history={customHistory}>
  ... app code ...
</CustomRouter>

Import and consume in your axios functions:

import axios from "axios";
import history from '../path/to/history';

export const api = axios.create({
  baseURL: "http://127.0.0.1:8000/",
  headers: {
    "content-type": "application/json",
  },
});

api.interceptors.response.use(
  function (response) {
    return response;
  },
  function (er) {
    if (axios.isAxiosError(er)) {
      if (er.response) {
        if (er.response.status == 401) {
          history.replace("/login"); // <-- navigate
        }
      }
    }

    return Promise.reject(er);
  }
);

Update

react-router-dom exports a history router, i.e. unstable_HistoryRouter.

Example:

import { unstable_HistoryRouter as HistoryRouter } from "react-router-dom";
import history from '../path/to/history';

...

<HistoryRouter history={customHistory}>
  ... app code ...
</HistoryRouter>

Note:

This API is currently prefixed as unstable_ because you may unintentionally add two versions of the history library to your app, the one you have added to your package.json and whatever version React Router uses internally. If it is allowed by your tooling, it's recommended to not add history as a direct dependency and instead rely on the nested dependency from the react-router package. Once we have a mechanism to detect mis-matched versions, this API will remove its unstable_ prefix.

like image 199
Drew Reese Avatar answered Sep 08 '25 01:09

Drew Reese


I had the same issue this is what I did and it worked for me ( based on an answer from a similar question for react router v4

I added a interceptor component inside of the react router config which passes a prop of the useNavigate hook

remove the interceptors from the axios config file and put them in a separate interceptors file

SetupInterceptor.js

//place all imports here

const SetupInterceptors = (navigate) => {
    axios.interceptors.response.use(
       function (response) {
           // Do something with response data
           return response;
       },
       function (error) {
           // Do something with response error
           if (error.response) {
                if (error.response.status === 401 || error.response.status === 403) {
                    navigate('/login');
    
                }
           }
           return Promise.reject(error);
      }
   );
};

export default SetupInterceptors;

axiosconfigfile.js

import axios from "axios";


//axios configuration here

export default axios;

add the AxiosInterceptorComponent to app.js

app.js

import SetupInterceptors from "SetupInterceptor";
import { useState } from "react";

function NavigateFunctionComponent(props) {
    let navigate = useNavigate();
    const [ran,setRan] = useState(false);

    {/* only run setup once */}
    if(!ran){
       SetupInterceptors(navigate);
       setRan(true);
    }
    return <></>;
}


function App(props) {
   return (
       <BrowserRouter>
           {<NavigateFunctionComponent />}
           <Routes>
              {/* other routes here */}
              <Route path="/login" element={<Login/>}></Route>
              {/* other routes here */}
           </Routes>
       </BrowserRouter>
   );
}
like image 31
user14665310 Avatar answered Sep 08 '25 00:09

user14665310