Open3

react + typescript で PrivateRoute コンポーネントを作ってみる

Yuma ShiraishiYuma Shiraishi
  • react + typescript
  • ログインしていなければログインページを表示する機能を Route に付与した PrivateRoute コンポーネントを作りたい
  • react 始めたてなので何も分からん
Yuma ShiraishiYuma Shiraishi

まず、RoutePrivateRouteはほぼ同じ機能を持ったコンポーネントであることから、継承すれば良いのでは無いかと考えて、以下のように実装した。型を付けるのにちょっと手こずった。

PrivateRoute.tsx
import { Route } from 'react-router-dom';
import { AuthContext } from './AuthProvider';
import Login from '../pages/LoginPage';

class PrivateRoute extends Route {
  static contextType = AuthContext;

  context!: React.ContextType<typeof AuthContext>;

  render(): JSX.Element {
    const { currentUser } = this.context;
    const Component = currentUser ? this.props.component : Login;

    return <Route {...this.props} component={Component} />;
  }
}

export default PrivateRoute;
Yuma ShiraishiYuma Shiraishi

しかし、継承ってあんまり react っぽくないのでは?という直感が働き、調べてみると公式ドキュメントに以下の様な記述を見つけた。

Facebook では、何千というコンポーネントで React を使用していますが、コンポーネント継承による階層構造が推奨されるケースは全く見つかっていません。

props とコンポジションにより、コンポーネントの見た目と振る舞いを明示的かつ安全にカスタマイズするのに十分な柔軟性が得られます。コンポーネントはどのような props でも受け付けることができ、それはプリミティブ値でも、React 要素でも、あるいは関数であってもよい、ということに留意してください。

ということなので、この記事などを参考に下記のように書き換えてみた。

PrivateRoute.tsx
mport { Route, RouteProps } from 'react-router-dom';
import { VFC } from 'react';
import { AuthContext } from './AuthProvider';
import LoginPage from '../pages/LoginPage';

const PrivateRoute: VFC<RouteProps> = ({ component: Component, ...rest }) => {
  return (
    <AuthContext.Consumer>
      {({ currentUser }) => {
        return currentUser ? (
          <Route component={Component} {...rest} />
        ) : (
          <LoginPage />
        );
      }}
    </AuthContext.Consumer>
  );
};

export default PrivateRoute;