ログインが完了したらトップページにリダイレクト、未ログインで認証が必要なページにアクセスしたらログインページへリダイレクト、という処理がある。
react-routerのv4でルーティングするとこんな形である。
App.js
import React, {Component} from 'react'
import {
BrowserRouter as Router,
Route, Switch
} from 'react-router-dom'
import Auth from './containers/Auth'
import Top from './containers/Top'
import Login from './containers/Login'
import Signup from './containers/Signup'
import Page from './containers/Page'
class App extends React.Component{
render(){
return(
<Router>
<Switch>
<Route exact path="/login" component={Login}/>
<Route exact path="/signup" component={Signup}/>
<Auth>
<Switch>
<Route exact path="/" component={Top}/>
<Route exact path="/page" component={Page}/>
</Switch>
</Auth>
</Switch>
</Router>
)}
}
export default App
このとき、Authコンポーネントでは
- 認証済みなら何もせず、配下のルーティングに処理をプロキシする(バケツリレーする)
- 認証がされていない(未ログイン)なら
/login
ページにリダイレクトする
ということをする。
stateのprops.currentUser.isLoggedIn
に認証状態を保持しているとして、
components/Auth.js
import React from 'react'
import { Redirect } from 'react-router-dom'
const Auth = (props) => (props.currentUser.isLoggedIn ? props.children : <Redirect to={'/Login'}/>)
export default Auth
結果は非常にシンプルなのだが、1. のバケツリレーがうまくいかず手間取った。NG集としては、
const Auth = (props) => (props.currentUser.isLoggedIn ? <Route children={props.children} /> : <Redirect to={'/Login'}/>)
const Auth = (props) => (props.currentUser.isLoggedIn ? <Route {...props} /> : <Redirect to={'/Login'}/>)
のように、プロキシのためにRouteコンポーネントを介すべきと思っていたら、これでは認証後のページでリダイレクトループが発生してしまう。props.children自体が、App.jsでAuthコンポーネントでラップしているSwitchコンポーネントだと気づいてうまくいった。
なお、Authコンポーネントの直下は単一のコンポーネントにしておく必要がある。今回はSwitchコンポーネントが該当する。バケツリレーするprops.childrenが直下に複数のコンポーネントを持ってしまうと、renderされるコンポーネントは単一の要素でなければいけないというreactの制約に怒られてしまうからだ。