はじめてのReact #24 「ルーティングに対応する」URLパラメーター編 react-router@v5

  • このエントリーをはてなブックマークに追加
  • LINEで送る

前回はReact Routerの基本的な機能を利用し簡単な図鑑アプリを作成しました。

今回はリクエストを受けたURLの一部をパラメーターとして受け取ってみます。
例えばブログやTwitterのようなアプリで特定のIDの記事を表示したい場合/posts/view/123といった形式のURLにアクセスするとします。ここで問題になるのは1000個の記事があった場合、1000個の<Route>を定義する必要が出てくるのかという点ですね。こういった場合に特定のパターンのURLは一部の文字列をパラメーターとして設定することができます。

- Sponsored Link -

名産品図鑑アプリを作る その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;
  }
}

複数同時にマッチするのを防ぐ

<Route>のpath属性の指定方法によっては、複数同時に実行されてしまう可能性があります。

  <Route path="/area/1" component={Home} />
  <Route path="/area/:cd" component={Meisan} />

これを防ぐためにはReactRouterの<Switch>を利用します。冒頭のimportSwitchの呼び出しを忘れずに。

<Switch>
  <Route path="/area/1" component={Home} />
  <Route path="/area/:cd" component={Meisan} />
</Switch>

<Route><Switch>の中に列挙することで、どれか一つだけが実行されます。上から順番に比較され最初にマッチした物が実行されるようです。逆にグローバルナビなど同時に実行したい物である場合は<Switch>の外に出しておく必要があります。

いずれにもマッチしない場合のルーティング

どのルーティングにもマッチしない場合は<Route>のpath属性を指定しないことで、いわゆる404 NotFound時の処理を定義することができます。

<Switch>
  <Route exact path="/" component={Home} />
  <Route path="/area/:cd" component={Meisan} />
  <Route component={NoMatch}/>
</Switch>

続き

参考ページ

- Sponsored Link -

同じカテゴリの記事

Donate

投げ銭お待ちしております!

BTC3A9nH1j7qQdKrSTrmnEdweo6zPqpHBmkxC
ETH0x1aE0541198D1F9f2908a25C35032A473e74D3731
XPXaQ9zv65F9ovfoMBrFGiPRG47aSHFhy8SX
MONAMTKgzSiS5BDueZkRCHySih24TGFwHThaDQ (MonaCoin)
ZNYZhnpf4RFYVQTAQiyoJg9dGoeC4bgT3BoSy (BitZeny)

ご質問やリクエストなどお気軽に。メールアドレスの入力は任意です。書き込みが反映されるまで時間がかかります。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください