前回はReact Routerの基本的な機能を利用し簡単な図鑑アプリを作成しました。
今回はリクエストを受けたURLの一部をパラメーターとして受け取ってみます。
例えばブログやTwitterのようなアプリで特定のIDの記事を表示したい場合/posts/view/123
といった形式のURLにアクセスするとします。ここで問題になるのは1000個の記事があった場合、1000個の
を定義する必要が出てくるのかという点ですね。こういった場合に特定のパターンのURLは一部の文字列をパラメーターとして設定することができます。
名産品図鑑アプリを作る その2
前回と同様に都道府県別に名産品を表示する簡単なWebアプリです。 実行結果は同じですが、コードを大幅に書き換えています。
実行結果
ソースコード
App.js
import React, { Component } from 'react'; import { BrowserRouter as Router, Route, Link, Switch } 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> </ul> {/* ここから下が実際のコンテンツに置き換わる */} <Switch> <Route exact path="/" component={Home} /> <Route exact path="/index.html" component={Home} /> <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> ); } /** * 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; }
解説
URLの一部をパラーメーターとして受け取る
今回のポイントは以下ですね。
<Route path="/area/:cd" component={Meisan} />
他のフレームワークなどでもよく見かける書式ですが、path属性を上記のように記述することで:cd
の部分にどのような文字列が来てもこのルーティング設定が適用されます。またここで:cd
にマッチした値はComponentにpropsとして渡されます。
例えば/area/123
へアクセスがあった場合、Meisan Componentではprops.match.params.cd
を参照することで123
の値を取り出すことができます。
class Meisan extends Component{ constructor(props){ props.match.params.cd; } }
複数同時にマッチするのを防ぐ
のpath属性の指定方法によっては、複数同時に実行されてしまう可能性があります。
<Route path="/area/1" component={Home} /> <Route path="/area/:cd" component={Meisan} />
これを防ぐためにはReactRouterの
を利用します。冒頭のimport
でSwitch
の呼び出しを忘れずに。
<Switch> <Route path="/area/1" component={Home} /> <Route path="/area/:cd" component={Meisan} /> </Switch>
を
の中に列挙することで、どれか一つだけが実行されます。上から順番に比較され最初にマッチした物が実行されるようです。逆にグローバルナビなど同時に実行したい物である場合は
の外に出しておく必要があります。
いずれにもマッチしない場合のルーティング
どのルーティングにもマッチしない場合は
のpath属性を指定しないことで、いわゆる404 NotFound
時の処理を定義することができます。
<Switch> <Route exact path="/" component={Home} /> <Route path="/area/:cd" component={Meisan} /> <Route component={NoMatch}/> </Switch>