前々回では基本的なReactRouterの使い方を、前回でURLの一部をパラメーターとして受け取ってみました。
今回はユーザーの状態に合わせてページの出し分けを行います。 具体的には
- 認証済みのユーザーにだけページを表示
- 未認証のユーザーにはログインページを表示
という仕様になります。
名産品図鑑アプリを作る その3
実行結果
謎の地域「?????」をクリックすると認証ページへリダイレクトさせられます。ここでログインボタンをクリックすると、ログイン状態になるので再度「?????」をクリックすると内容が表示されます。 s3-us-west-2.amazonaws.com
ソースコード
App.js
import React, { Component } from 'react'; import { BrowserRouter as Router, Route, Link, Switch, Redirect } from "react-router-dom"; import './App.css'; class App extends Component { render() { return ( <Router> <div className={"container"}> {/* ナビゲーション */} <ul className={"gnavi"}> <li><Link to="/">名産品図鑑</Link></li> <li>┣ <Link to="/area/1">埼玉県</Link></li> <li>┣ <Link to="/area/2">島根県</Link></li> <li>┗ <Link to="/private">?????</Link></li> <li> </li> <li><Link to="/auth">認証</Link></li> </ul> {/* ここから下が実際のコンテンツに置き換わる */} <Switch> <Route exact path="/" component={Home} /> <Route exact path="/index.html" component={Home} /> <Route exact path="/auth" component={Auth} /> <PrivateRoute path="/private" component={PrivatePage} auth="/auth" /> <Route path="/area/:cd" component={Meisan} /> <Route component={NoMatch}/> </Switch> </div> </Router> ); } } /** * トップページ */ function Home(){ return ( <div className={"item"}> <h2>名産品図鑑</h2> <ul> <li><Link to="/area/1">埼玉県<br /><img src="/image/saitama.png" /></Link></li> <li><Link to="/area/2">島根県<br /><img src="/image/shimane.png" /></Link></li> </ul> </div> ); } /** * 認証状態を保持する変数 */ var UserStatus = { auth: false //true:ログイン, false:未ログイン }; /** * 簡易的な認証ページ */ class Auth extends Component{ constructor(props){ super(props); this.state = { auth: UserStatus.auth } this.onLogin = this.onLogin.bind(this); this.onLogout = this.onLogout.bind(this); } onLogin(e){ e.preventDefault(); this.setState({auth:true}); UserStatus.auth = true; } onLogout(e){ e.preventDefault(); this.setState({auth:false}); UserStatus.auth = false; } render(){ let desc = UserStatus.auth? "現在ログイン中です":"ログインしていません"; let button = UserStatus.auth? <button onClick={this.onLogout}>ログアウト</button>:<button onClick={this.onLogin}>ログイン</button>; return ( <div className={"item"}> <h2>認証ページ</h2> <p>{desc}</p> <form> {button} </form> </div> ); } } /** * ログインチェック付きRouter */ class PrivateRoute extends Component{ constructor(props){ super(props); } render(){ if( UserStatus.auth ){ return( <Route path={this.props.path} component={this.props.component} /> ); } else{ return( <Redirect to={this.props.auth} /> ); } } } /** * 秘密のページ */ function PrivatePage(){ return( <div className={"item"}> <h2>南極</h2> <img src="/image/nankyoku.png" /> <ul> <li>ぺんぎん</li> <li>アザラシ</li> <li>オーロラ</li> </ul> </div> ); } /** * 404 */ function NoMatch(){ return ( <div className={"item"}> <h2>URLが存在しません</h2> </div> ); } /** * 名産品 Component */ class Meisan extends Component{ constructor(props){ super(props); this.area =[ {cd:1, name:"埼玉県", img:"/image/saitama.png", meisan:["十万石まんじゅう", "深谷ねぎ", "草加せんべい", "いも"]}, {cd:2, name:"島根県", img:"/image/shimane.png", meisan:["しじみ", "あご野焼", "出雲そば", "ぶどう"]} ]; } render(){ let cd = this.props.match.params.cd; if( (0 < cd) && (cd <= this.area.length) ){ let area = this.area[cd - 1]; let key = 1; let li = area.meisan.map( val => <li key={key++}>{val}</li> ); return ( <div className={"item"}> <h2>{area.name}</h2> <img src={area.img} /> <ul> {li} </ul> </div> ); } else{ return ( <div className={"item"}> <h2>Not Found</h2> </div> ); } } } export default App;
App.css
CSSは前回から変更はありません。
a:hover{ color: red; } ul{ list-style: none; } .container{ display: inline-flex; /* 横に並べる */ } .gnavi{ order: 1; width: 80px; height: 500px; padding: 10px; margin-right: 10px; background-color: skyblue; } .item{ order: 2; }
解説
認証チェックは独自実装が必要
ReactRouterには認証状態をチェックしてルーティングする機能が存在しないため、独自に実装する必要があるようです。
今回は
Componentを新規に作成しました。
<Switch> <PrivateRoute path="/private" component={PrivatePage} auth="/auth" /> </Switch>
このComponentが行っていることは非常にシンプルです。
認証状態を保持しているグローバル変数がtrue
になっていれば
を、false
なら
を返しているだけです。
class PrivateRoute extends Component{ constructor(props){ super(props); } render(){ if( UserStatus.auth ){ return( <Route path={this.props.path} component={this.props.component} /> ); } else{ return( <Redirect to={this.props.auth} /> ); } } }
ログイン状態の切り替えは、こちらも簡易的に作成した
Componentで行っています。ボタンを押したら認証状態を保持しているグローバル変数のtrue
とfalse
が入れ替わるだけのシンプルな物です。実際のアプリではここでサーバと通信して認証結果を取ってくることになります。
クラスでComponentを作成している関係で若干コードが長くなっていますが、原理としては非常に簡単ですね。