web-dev-qa-db-ja.com

Reactコンポーネント外の履歴オブジェクトにアクセスする方法

まず、私は withRouter HoCにかなり精通していますが、この場合、コンポーネント内のhistoryオブジェクトにアクセスしたくないので、役に立ちません。

APIエンドポイントから401を受信した場合にユーザーをログインページにリダイレクトするメカニズムを実現しようとしています。 httpリクエストを行うために axios を使用しています。カバーする必要のある約60のエンドポイントがあり、それらはアプリ全体の多数のコンポーネントで使用されています。

私はaxiosインスタンスオブジェクトにデコレータ関数を作成したいと思います。

1. makes the request
2. if fail && error_code = 401, update user route to `/login`
3. if success, return promise

上記の問題は、ユーザーのルートを更新することです。以前は、react-router-v3、react-routerパッケージからbrowserHistoryオブジェクトを直接インポートできた可能性がありますが、これはできなくなりました。

だから、私の質問は、Reactコンポーネントの外で履歴オブジェクトにアクセススタックを介さずにアクセスするにはどうすればよいですか?

25
Dragos Rizescu

react-router v4は、historyパッケージ、つまりcreateBrowserHistory()関数を介して履歴を共有する方法も提供します。

重要な部分は、同じ履歴オブジェクトがアプリ全体で共有されるようにすることです。そのためには、ノードモジュールがシングルトンであるという事実を利用できます。

プロジェクトにhistory.jsというファイルを作成し、次の内容を含めます。

import { createBrowserHistory } from 'history';

const history = createBrowserHistory();
export default history;

その後、次の方法でアプリケーションにインポートできます。

import history from "./history.js";

Routerのみがhistoryプロップを受け入れる(BrowserRouterは受け入れない)ことに注意してください。したがって、ルータJSXを適宜更新してください。

import { Router } from "react-router-dom";
import history from "./history.js";

// and then in your JSX:
return (
  <Router history={history}>
    {/* routes as usuall */}
  </Router>
)

実際の例は https://codesandbox.io/s/owQ8Wrk にあります

28
BTMPL

同じ問題が発生しました。この問題を解決するために使用した解決策を次に示します。

最終的に、すべてのサービス関数を持つオブジェクトを返すファクトリ関数を作成しました。このファクトリ関数を呼び出すには、以下の形状のオブジェクトが必要です。

interface History {
    Push: (location: string) => void;
}

これが私のファクトリー関数の蒸留バージョンです。

const services = {};

function servicesFactory(history: History) {
    const countries = countriesFactory(history);
    const local = {
        ...countries,
    };
    Object.keys(local).forEach(key => {
        services[key] = local[key];
    });

}

この関数が定義されているファイルは2つのものをエクスポートします。

1)このファクトリー機能

2)サービスオブジェクト。

これは国のサービスがどのように見えるかです。


function countriesFactory(h: History): CountriesService {
    const countries: CountriesService = {
        getCountries() {
            return request<Countries>({
                method: "get",
                endpoint: "/api/countries",
            }, h)
        }
    }
    return countries;
}

そして最後に、私のrequest関数は次のようになります。

function request<T>({ method, endpoint, body }: Request, history: History): Promise<Response<T>> {
    const headers = {
        "token": localStorage.getItem("someToken"),
    };
    const result: Response<T> = {
        data: null,
        error: null,
    };
    return axios({
        url: endpoint,
        method,
        data: body,
        headers,
    }).then(res => {
        result.data = res.data;
        return result;
    }).catch(e => {
        if (e.response.status === 401) {
            localStorage.clear();
            history.Push("/login");
            return result;
        } else {
            result.error = e.response.data;
            return result;
        }
    });
}

ご覧のとおり、request関数を実行すると、履歴オブジェクトが渡され、サービスから取得されます。サービスは、サービスファクトリから取得します。

ここで素晴らしいのは、このファクトリー関数を呼び出して、履歴オブジェクトをアプリ全体で一度だけ渡すだけで済むことです。その後、単純にservicesオブジェクトをインポートして、履歴オブジェクトを渡すことを心配することなく、そのオブジェクトで任意のメソッドを使用できます。

サービスファクトリ関数を呼び出すコードは次のとおりです。

const App = (props: RouteComponentProps) => {
  servicesFactory(props.history);
  return (
    // my app and routes
  );
}

この質問を見つけた他の誰かがこれが役立つことを願っています。

1
Chaim Friedman