簡體   English   中英

React路由器v6如何在axios攔截器中使用`navigate`重定向

[英]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);
  }
);

在 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_ _前綴。

我遇到了同樣的問題,這就是我所做的,它對我有用(基於對 react router v4 的類似問題的回答

我在反應路由器配置中添加了一個攔截器組件,它傳遞了 useNavigate 鈎子的道具

從 axios 配置文件中刪除攔截器並將它們放在單獨的攔截器文件中

安裝攔截器.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;

將 AxiosInterceptorComponent 添加到 app.js

應用程序.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>
   );
}

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

這不起作用的原因是因為您只能在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 調用作為回調傳遞給鈎子。

對於從 v5 遷移到 v6 的 react 路由器的參考,此視頻很有幫助

從 6.1.0 版本開始,React Router 有一個unstable_HistoryRouter的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_ -prefix 是什么意思時,其中一位維護者回答

不穩定的前綴只是表示您可能會遇到由於 react-router 請求本身的歷史版本不匹配而導致的錯誤。 雖然實際影響可能只是產生類型問題(歷史版本之間的不同類型),但也可能存在導致更細微錯誤的 API 或行為更改(例如如何解析 URL,或如何生成路徑)。

只要您密切關注 react-router 請求的歷史版本並確保它與您的應用程序使用的版本同步,您就應該沒有問題。 我們還沒有針對不匹配的保護或警告,因此我們現在稱之為不穩定。

有一些關於 react-router-v4 的很好的答案,我認為這對一些修改最少的人非常有用。

如何從 axios-interceptor-with-react-router-v4 重定向

我通過為攔截器創建一個組件解決了這個問題:

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;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM