reactjs 如何在axios拦截器中使用'导航'重定向

vaj7vani  于 2023-01-02  发布在  React
关注(0)|答案(6)|浏览(270)
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);
  }
);
jpfvwuh4

jpfvwuh41#

在RRDv6之前的世界里,你会创建一个自定义的history对象,导出、导入并传递给Router,导入并在外部javascript逻辑中访问,比如redux-thunks、axios实用程序等。
要在RRDv6中复制这一点,您还需要 * 创建一个自定义路由器组件,以便可以向其传递外部history对象。这是因为所有更高级别的RRDv6路由器都维护自己的内部历史上下文,因此我们需要复制历史示例化和状态部分,并传递props以适应基本Router组件的新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}
    />
  );
};

创建所需的历史对象:

import { createBrowserHistory } from "history";

const history = createBrowserHistory();

export default history;

导入并传递到新的CustomRouter

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

...

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

在axios函数中导入和使用:

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);
  }
);

更新

react-router-dom导出历史路由,即unstable_HistoryRouter
示例:

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

...

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

注:
此API当前的前缀为unstable_,因为您可能会无意中向应用添加两个版本的history库,一个是添加到package.json的版本,另一个是React Router内部使用的版本。建议不要将history添加为直接依赖项,而是依赖react-router包中的嵌套依赖项。匹配的版本,则此API将删除其unstable_前缀。

uttx8gqw

uttx8gqw2#

我也遇到了同样的问题,这就是我所做的,它对我很有效(based on an answer from a similar question for react router v4
我在react路由器配置中添加了一个拦截器组件,它传递useNavigate钩子的属性
从axios配置文件中删除拦截器,并将它们放在单独的拦截器文件中
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;

将AxiosInterceptor组件添加到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>
   );
}
bybem2ql

bybem2ql3#

v6.1.0开始,您可以轻松地在react组件之外进行重定向。

import {createBrowserHistory} from 'history';
import {unstable_HistoryRouter as HistoryRouter} from 'react-router-dom';

let history = createBrowserHistory();

function App() {
  return (
    <HistoryRouter history={history}>
      // The rest of your app
    </HistoryRouter>
  );
}

// Usage
history.replace('/foo');

只需将BrowserRouter替换为HistoryRouter即可。
酱料酱:https://github.com/remix-run/react-router/issues/8264#issuecomment-991271554

omtl5h9j

omtl5h9j4#

我已经通过创建拦截器组件解决了这个问题:

import { useEffect, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import { axiosInstance } from '../custom-axios';

export const ResponseInterceptor = () => {
  const { onLogout } = useAuth();
  const { addError } = useAPIError();
  const navigate = useNavigate()

  const interceptorId = useRef<number | null>(null);

  useEffect(() => {
    interceptorId.current = axiosInstance.interceptors.response.use(undefined, (error) => {
      switch (error.response.status) {
        case 401:
          navigate('/login');
          break;
      }
    });

    return () => {
      axiosInstance.interceptors.response.eject(interceptorId.current as number);
    };
  }, []);

  return null;
};

并将其连接到App组件:

const App = () => {
  return (
    <AuthProvider>
      <APIErrorProvider>
        <AppRoutes />
        <ResponseInterceptor />
        <APIErrorNotification />
      </APIErrorProvider>
    </AuthProvider>
  );
};
export default App;
lnvxswe2

lnvxswe25#

自版本6.1.0起,React Router有一个unstable_HistoryRouter。有了它,history现在可以再次在React上下文之外使用。它的用法如下:

// lib/history.js
import { createBrowserHistory } from "history";
export default createBrowserHistory();

// App.jsx
import { unstable_HistoryRouter as HistoryRouter } from 'react-router-dom';
import history from "lib/history";

function App() {
  return (
    <HistoryRouter history={history}>
      // The rest of your app
    </HistoryRouter>
  );
}

// lib/axios.js
import history from "lib/history";
import storage from "utils/storage";

export const axios = Axios.create({})

axios.interceptors.response.use(
  (response) => {
    return response.data;
  },
  (error) => {
    if (error.response?.status === 401) {
      storage.clearToken();
      history.push("/auth/login");
      return Promise.resolve();
    }

    return Promise.reject(error);
  }
);

来源
当被问到unstable_-前缀是什么意思时,其中一个维护人员回答说:
unstable_ prefix只是表示您可能会遇到错误,因为历史版本与React路由器本身请求的历史版本不匹配。虽然实际影响可能只是创建类型问题(不同历史版本之间的不同类型),但也可能有API或行为更改导致更微妙的错误(如如何解析URL,或如何生成路径)。
只要你时刻关注react-router请求的历史版本,并确保它与你的应用使用的版本同步,你就不会遇到问题。我们还没有针对不匹配的保护或警告,因此我们暂时称之为不稳定。

hmmo2u0o

hmmo2u0o6#

这不起作用的原因是因为您只能使用React组件定制钩子内的钩子。
由于两者都不是,因此useNavigate()钩子失败。
我的建议是将整个逻辑封装在一个定制钩子中。
伪码:

import { useCallback, useEffect, useState } from "react"

export default function useAsync(callback, dependencies = []) {
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState()
  const [value, setValue] = useState()

  // Simply manage 3 different states and update them as per the results of a Promise's resolution
  // Here, we define a callback
  const callbackMemoized = useCallback(() => {
    setLoading(true)
    setError(undefined)
    setValue(undefined)
    callback()
      // ON SUCCESS -> Set the data from promise as "value"  
      .then(setValue)
      // ON FAILURE -> Set the err from promise as "error"  
      .catch((error)=> {
          setError(error)
          useNavigate()('/login')
       })
      // Irresp of fail or success, loading must stop after promise has ran
      .finally(() => setLoading(false))
      // This function runs everytime some dependency changes
  }, dependencies)

  // To run the callback function each time it itself changes i.e. when its dependencies change
  useEffect(() => {
    callbackMemoized()
  }, [callbackMemoized])

  return { loading, error, value }
}

在这里,只需将axios调用作为回调传递给钩子。
对于react router从v5迁移到v6的参考,这个video是有帮助的

相关问题