2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[React,TypeScript,react-router] しっかり型定義して、未認証ユーザーはリダイレクトするPrivateRouteをwithRouterで作る

Last updated at Posted at 2021-12-07

はじめに

jsで書かれた少し古めの技術書を写経する際に、withRouterを使った実装をTypeScriptで置換したのでメモ。
withRouterではなくuseHisotryを使った方法は記事末尾に記載。
react-routerについては最新のv6による記法ではないです。

この記事でできるようになること

  • react-routerでTypeScriptで型定義をした上でwithRouterを使えるようになる
  • Routeコンポーネントをラップして認証していないユーザーを特定のページへリダイレクトするPrivateRouteを作成する。

イメージ

const App = () => {
    return (
        <Router>
                <Switch>
                <Route exact path="/" component={Home} />
                <Route path="/signup" component={SignUp} />
                <Route path="/signin" component={SignIn} />
                //↓未認証ユーザーは別ルートへリダイレクトする
                <PrivateRoute path="/new" component={NewPost} />  
                </Switch>
        </Router>
    );
};

withRouterとは?

react-routerが提供しているHOC(高階コンポーネント)。
withRouter(Hoge)のようにコンポーネントを包むことで、Routeコンポーネントが受け取るようなhisotrylocationなどをpropsとしてラップしたコンポーネント(Hoge)に渡すことができる。
認証処理に失敗したときにリダイレクトを行うにはhistoryが必要なので今回利用している。

実は、同じくreact-routerが提供しているuseHisotryを使えばwithRouterを使わずともhistoryにアクセスできるが、今回は技術書コードに型を付けたかっただけなので選択肢として選ばなかった。

コンポーネントの作成

必要なpropsの洗い出し

今回作成するPrivateRouteコンポーネントが持つべきpropsは以下

  • 認証に用いるwithRouterから渡されるprops群
    • hisotry,location etc
  • ラップするRouteコンポーネントに渡すprops群
    • path, component etc

型を探す

withRouterで渡されるprops群

react-router-domの中にRouteComponentPropsが用意されているのでそれを利用する。

export interface RouteComponentProps<
    Params extends { [K in keyof Params]?: string } = {},
    C extends StaticContext = StaticContext,
    S = H.LocationState
> {
    history: H.History<S>;
    location: H.Location<S>;
    match: match<Params>;
    staticContext?: C | undefined;
}

こちらの記事が参考になった。

ラップするRouteコンポーネントに渡すprops群

Routeコンポーネントの定義をあさって、props用の型RoutePropsを発見したのでこれを使う。

export interface RouteProps<
    Path extends string = string,
    Params extends { [K: string]: string | undefined } = ExtractRouteParams<Path, string>
> {
    location?: H.Location | undefined;
    component?: React.ComponentType<RouteComponentProps<any>> | React.ComponentType<any> | undefined;
    render?: ((props: RouteComponentProps<Params>) => React.ReactNode) | undefined;
    children?: ((props: RouteChildrenProps<Params>) => React.ReactNode) | React.ReactNode | undefined;
    path?: Path | readonly Path[] | undefined;
    exact?: boolean | undefined;
    sensitive?: boolean | undefined;
    strict?: boolean | undefined;
}

今回はpath,componentを渡すだけなので以下のようにしても良い。

{path:string,component:React.ComponentType<any>}

作成

認証処理の具体的な内容は使っている認証基盤によっても異なるので今回は割愛。

type PrivateRouteProps = RouteComponentProps & RouteProps
// あるいは
// PrivateRouteProps = RouteComponentProps & {path:string,component:React.ComponentType<any>}
const PrivateRoute = withRouter(({history,path,component}:PrivateRouteProps) => {
    
    // ここに認証処理を書く
    // 仮に認証の成否をisAuthenticatedに入れるとすると以下のように書くことができる
    // 
    if(!isAuthenticated) {
        history.push('/signIn');//signInページにリダイレクトする
    }
    return (
        <Route
            path={path}
            component={component}
        />
    );

});

おまけ

useHisotryを使う場合は以下。

import {useHisotry} from 'react-router-dom';

type PrivateRouteProps = RouteProps

const PrivateRoute = ({path,component}:PrivateRouteProps) => {
    
    // ここに認証処理を書く
    // 仮に認証の成否をisAuthenticatedに入れるとすると以下のように書くことができる
    // 
    if(!isAuthenticated) {
        history.push('/signIn');//signInページにリダイレクトする
    }
    return (
        <Route
            path={path}
            component={component}
        />
    );

});

参考

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?