LoginSignup
95
79

More than 1 year has passed since last update.

Reactでログイン機能を実装する

Last updated at Posted at 2020-11-08

はじめに

react-router-domでPrivateRouteを実現します。今回は以下の機能を実装していきたいと思います。

  1. ログイン済みユーザーしかアクセスできないページを作る
  2. ↑のページに、ログインせずにアクセスしようとするとログインページにリダイレクトされる。
  3. 2でログインページにリダイレクトされた後、ログインを行えば元のページ(アクセスしたかったページ)に遷移する。

画面構成は以下のようにします。

  • /login: ログイン画面。未ログインユーザーしかアクセスできない。
  • /: ホーム画面。ログイン済みユーザーしかアクセスできない。
  • /profile/:userId: ユーザーのプロフィール画面。ログイン済みユーザーしかアクセスできない。

プロジェクトはGithubのレポジトリにあげているのでよければご覧ください。

プロジェクトを作る

今回はcreate-react-appを用いてセットアップします。次にreact-router-domをインストールします

npx create-react-app private-router-sample --template typescript
npm install react-router-dom @types/react-router-dom

PrivateRouteを実装する

今回の肝となるPrivateRouteの実装です。(useAuthUser, AuthUserProviderについては別ファイルで定義しています。詳しい実装はGithubをご覧ください)

App.tsx
import React from 'react';
import { BrowserRouter as Router, Redirect, Route, RouteProps, Switch } from 'react-router-dom';

// PrivateRouteの実装
const PrivateRoute: React.FC<RouteProps> = ({...props}) => {
  const authUser = useAuthUser()
  const isAuthenticated = authUser != null //認証されているかの判定
  if (isAuthenticated) {
    return <Route {...props}/>
  }else{
    console.log(`ログインしていないユーザーは${props.path}へはアクセスできません`)
    return <Redirect to="/login"/>
  }
}

const App: React.FC = () => {
  return (
    <AuthUserProvider>
      <Router>
        <Switch>
          <Route exact path="/login" component={LoginPage}/>

         {/* Routeと同じ形で使用 */}
          <PrivateRoute exact path="/" component={HomePage} /> 
          <PrivateRoute exact path="/profile/:userId" component={ProfilePage}/>
        </Switch>
      </Router>
    </AuthUserProvider>
  );
}

ポイントは、PrivateRoute内の以下の実装です。

if (isAuthenticated) {
  return <Route {...props}/>
} else {
  console.log(`ログインしていないユーザーは${props.path}へはアクセスできません`)
  return <Redirect to="/login"/>
}

認証済身ならRouteを、認証されていなければRedirectを返すことでログインページへのリダイレクトを行っています。

UnAuthRoute

上記の実装では、「未ログインのユーザーが/にアクセス」することは防げましたが、「ログイン済みのユーザーが/loginにアクセス」することは防げていません。そこで、ログイン済みユーザーが/loginへアクセスした場合、/へリダイレクトする処理を実装します。

今回はPrivateRouteと逆の処理を行うUnAuthRouteを実装したいと思います。

App.tsx
import React from 'react';
import { BrowserRouter as Router, Redirect, Route, RouteProps, Switch } from 'react-router-dom';

// PrivateRouteの実装
const PrivateRoute: React.FC<RouteProps> = ({...props}) => {
  const authUser = useAuthUser()
  const isAuthenticated = authUser != null //認証されているかの判定
  if (isAuthenticated) {
    return <Route {...props}/>
  }else{
    console.log(`ログインしていないユーザーは${props.path}へはアクセスできません`)
    return <Redirect to="/login"/>
  }
}

// ===追加部分 ==========
// UnAuthRouteの実装
const UnAuthRoute: React.FC<RouteProps> = ({ ...props }) => {
  const authUser = useAuthUser()
  const isAuthenticated = authUser != null
  if (isAuthenticated) {
    console.log(`ログイン済みのユーザーは${props.path}へはアクセスできません`)
    return <Redirect to="/" />
  } else {
    return <Route {...props} />
  }
}
// ===追加部分ここまで ======

const App: React.FC = () => {
  return (
    <AuthUserProvider>
      <Router>
        <Switch>
         {/* 未ログインユーザーのみアクセス可能 */}
          <UnAuthRoute exact path="/login" component={LoginPage}/>
         {/* ログイン済みユーザーのみアクセス可能 */}
          <PrivateRoute exact path="/" component={HomePage} /> 
          <PrivateRoute exact path="/profile/:userId" component={ProfilePage}/>
        </Switch>
      </Router>
    </AuthUserProvider>
  );
}

元のページへリダイレクト

ここまでの実装では、3の要件を満たしていません。

例えば、未ログインのユーザーが/profile/1にアクセスしたときの処理の流れは以下のようになります

1. /profile/1にアクセス
2. PrivateRouteによって/loginにリダイレクトされる
3. ログインが完了すればUnAuthRouteによって/にリダイレクトされる ← 要件を満たすならば`/profile/1`へリダイレクトされるべきです。

そこで、PrivateRouteUnAuthRouteを以下のように修正します。

App.tsx
const PrivateRoute: React.FC<RouteProps> = ({...props}) => {
  const authUser = useAuthUser()
  const isAuthenticated = authUser != null
  if (isAuthenticated) {
    return <Route {...props}/>
  }else{
    console.log(`ログインしていないユーザーは${props.path}へはアクセスできません`)
    return <Redirect to={{pathname: "/login", state: { from: props.location?.pathname }}}/> // fromに本来アクセス
  }
}

const UnAuthRoute: React.FC<RouteProps> = ({ ...props }) => {
  const authUser = useAuthUser()
  const isAuthenticated = authUser != null
  const { from } = useLocation<{from: string | undefined}>().state

  if (isAuthenticated) {
    console.log(`ログイン済みのユーザーは${props.path}へはアクセスできません`)
    return <Redirect to={from ?? "/"} />
  } else {
    return <Route {...props} />
  }
}

実装のポイントはPrivateRouteの <Redirect to={{pathname: "/login", state: { from: props.location?.pathname }}}/>です。PrivateRouteは未ログインユーザーをログインページにリダイレクトしますが、その際にユーザーがアクセスしようとしたURLをstateとして設定しています。

ここで設定したstateはUnAuthRouteで const { from } = useLocation<{from: string | undefined}>().stateという形で取り出されます。取り出されたURLは、return <Redirect to={from ?? "/"} />でリダイレクト先として指定されます。(?? "/"が指定されているのは、ユーザーが直接ログインページにアクセスした場合はfrom == nullとなるからです)

最後に

今回はログイン/未ログインの2状態を扱いましたが、サービスによってはユーザー登録が絡む場合もあるでしょう。その場合には、未ログイン/未登録/登録済みの3状態を扱うことになりますが、UnRegisteredRouteを追加するなどで応用できます。

95
79
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
95
79