ルーティングって何で必要で、何してるの?を React Routerで理解する


はじめに

フロントエンド開発が、正直苦手でした。
でも今回のプロジェクトでReactを触ることになり、「せっかくなら苦手意識を克服したい」と一念発起。
開発は終わりましたが、学習は継続しています。

Reactには多くの要素がありますが、今回はルーティングに絞ります。
バージョンの違いで書き方が変わり対応に戸惑ったこともあり、基本から振り返りつつ理解していった流れを記事にしたいと思います。

そもそもルーティングって何のために必要なの?

まず基礎の基礎になりますが、URL、ブラウザ、SPAの観点から振り返ります。

URLとは?

Uniform Resource Locator(統一資源位置指定子)の略語で、要するに「インターネット上のモノの住所」です。
https://example.com/products.html
このようなURLの場合、ブラウザは、
  1. URLを見る→(products.htmlというファイルが欲しいのだと解釈)
  2. サーバーにリクエストを送る
  3. HTMLファイル(products.html)をダウンロードして表示する
つまり、URLは「どのファイルを取得するか」を指定する住所のようなものです。

ブラウザはURLで何をしているのか?

ブラウザにとってURLは「ページの識別子」です。
ブラウザは、URLをキーにして、いろんな機能を実現しています。
    1. 履歴管理
        URLが変わるたびに履歴に記録
        「戻る」ボタンを押すと、履歴から1つ前のURLを取り出して、そのページに戻る
        「進む」ボタンも同じ仕組み
    2. ブックマーク
        URLを保存することで、「このページ」を記憶
        後でそのURLを開けば、同じページが表示される 
    3. URL共有
        誰かにURLを送る「このページを見て」と伝えられる
        URLが同じなら、同じページが開く 
    4. リロード
        ブラウザの更新ボタンを押すと、今のURLを使ってページを再取得

SPA(Single Page Application)の特徴と課題 

SPA(Single Page Application)についても簡単に整理します
    過去のWebサイト(マルチページアプリケーション)
    • 各ページごとに別々のHTMLファイルが存在
    • ページ遷移のたびにサーバーにリクエストを送り、新しいHTMLを取得
    • 画面全体がリロードされる
    SPAのWebサイト
    • HTMLファイルは1つだけ
    • JavaScriptで画面を動的に書き換える
    • ページリロードなしで高速に画面を切り替えられる
    一見すると良いことずくめに見えますが、ここに課題があります。

HTMLファイルが1つしかないということは?
画面が切り替わってもURLは変わらない、URLが変わらないと、ブラウザは「ページ遷移していない」と判断する。
その結果、上記で挙げたブラウザの基本機能が実現できない。
  • URLが変わらないと履歴に記録されない → 戻るボタンが使えない(前に見ていた別のサイトに戻ってしまう)
  • ブックマークしても意味がない(常にトップページが開く)
  • URLを共有できない(開いても別のページが表示される)
ここで、ルーティング登場。

ルーティングの基本的な仕組み

React Routerの役割

上記で記載したSPAの課題を解決するための仕組みとしてルーティング機能が存在しています。
それを踏まえると、ルーティングは何をしているのか?
  1. URLを変更して履歴に記録する
  2. そのURLに応じて、どのコンポーネントを表示するか判断する
簡単に言えばこれだけ。

主要なコンポーネント

import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
      </nav>
      
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>
    </BrowserRouter>
  );
}

BrowserRouter

<Link>や<Route>が実際のパスを指定して、このURLならこのコンポーネントという紐付けを指定しています。
では、このLinkやRouteはどうやって「現在のURLが何か」を知ることができるのか?
それを教えているのがBrowserRouterです。
BrowserRouterは、現在のURLの情報をすべての子コンポーネントに共有するReactの用語で言うとContext(コンテキスト)を提供しています。
BrowserRouterがないと、現在のURLがわからずエラーとなってしまいます。

気づき💡
これはReact Routerに限ったことではなく、他のライブラリでも何かしらのProviderで囲むというパターンがよく出てきます。
ここで、文章で読んでもピンときてなかった、Reactの単一方向データフローや各コンポーネントへのデータの渡し方という設計思想が何となく分かった気がしました😀

Routes/Route

Routesは配下のRouteを管理して、現在のURLに該当するRouteを選択する役割、
Routeは選択されたらURLに紐づくコンポーネントを表示する役割。
具体的な動きを見てみます。現在のURLが/aboutだとする。
Routes
  • 配下の複数のRouteを見渡す
  • 現在のURL(`about)と各Routeのpathを照合
  • マッチするRouteを1つ選択する
Route
  • 自分の担当するpath(path="/about")を宣言
  • そのpathに紐づくコンポーネント(element={<About />})を定義
  • Routesから選択されたら、そのコンポーネントを表示
    ※今回はpath="/about"のRouteが選択され、紐づくコンポーネントAboutコンポーネントが表示される

Link

画面上にリンクを表示します。ユーザーがクリックすることで紐づくページに遷移します。
<a>タグも同様に画面上にリンクを表示、ユーザーがクリックすることで紐づく画面に遷移します。
では<Link><a>タグでは、何が違うのか?
<a>タグを使用した場合
<a href="/about">About</a>
リンクがクリックされると
  • ブラウザは/aboutにアクセスしようとする
  • サーバーにHTTPリクエストを送る
  • ページ全体がリロードされる
ページをリロードせずに画面を切り替えるSPAの良さが失われてしまいます。

Linkを使うと何が違うのか?
<Link to="/about">About</Link>
リンクがクリックされると
  • React RouterがURLを変更、ブラウザの履歴に記録する
  • Routes/RouteがURLに応じたコンポーネントを表示
サーバーにリクエストを送らず、ページリロードなしで画面を切り替え、SPAの利点を活かせます。

主要なフック

ルーティングを実現するうえで使用される主要なフックについて見ていきます。

useNavigate

Linkと同じく画面遷移を実現しますが、決定的な違いがあります。
Linkはユーザーのクリック操作で遷移する。useNavigateはプログラムで遷移を実行する。
この違いが重要になるのは、以下のような場面です。
  1. 何か処理をした後に遷移したい
    フォーム送信、データ保存などの処理完了後に次のページへ進む場合
  2. 条件によって遷移先を変えたい
    処理結果に応じて、成功ページかエラーページか動的に決定する場合
  3. 条件によって遷移するか決めたい
    バリデーションエラーがあれば遷移せず、エラーがなければ遷移する場合
// 例1:処理結果に応じた動的な遷移
const handleSubmit = () => {
  const result = doSomething();
  
  if (result.success) {
    navigate('/success');  // 成功ページへ
  } else {
    navigate('/error');    // エラーページへ
  }
};

// 例2:バリデーションで遷移を制御
const handleNext = () => {
  if (!isFormValid()) {
    setError('入力内容を確認してください');
    return;  // エラーがあれば遷移しない
  }
  navigate('/confirm');  // エラーがなければ遷移
};
要するに、「JavaScript関数内で遷移をコントロールしたい」全ての場面で使用します

useLocation

現在のURL情報を取得するために使用します。
画面遷移に限らず、URL情報を活用するあらゆる場面で使われます。

useLocationで取得できるもの   例:URLが /products?id=123 の場合
 pathname :  '/products' // URLのパス部分
 search   :  '?id=123' // クエリパラメータ
 state    :  { from: 'list' } // 遷移時に渡されたデータ

具体的な使用例

1. ナビゲーションのハイライト表示
現在表示しているページのリンクだけ、見た目を変えたい場合
const location = useLocation();

<nav>
  <Link className={location.pathname === '/' ? 'active' : ''}>
    Home
  </Link>
  <Link className={location.pathname === '/about' ? 'active' : ''}>
    About
  </Link>
</nav>
2. クエリパラメータの取得
検索ページなど、URLから条件を読み取る場合
// URLが /search?keyword=react&category=frontend の場合

const SearchPage = () => {
  const location = useLocation();
  const params = new URLSearchParams(location.search);
  
  const keyword = params.get('keyword');      // 'react'
  const category = params.get('category');    // 'frontend'
  
  // この値を使って検索を実行
  useEffect(() => {
    fetchSearchResults(keyword, category);
  }, [keyword, category]);
  
};
このように「URL」という「状態」を読み取って、それに応じた処理を実現します

最後に

今回は、React Router v7を前提にルーティングの基本を整理しました。
学習し始めたのがv5で少し古かったので、v7のコードで「ん?Switch消えてる?」など混乱することもあり、ブラウザとURLの関係から整理、復習し直すことで、各コンポーネントやフックの存在意義がしっかりと理解できたと思います。

実際のプロジェクトでは、Next.jsを組み合わせて使用することが多く、ルーティングもNext.jsのファイルベースルーティングで実現されていることが多いです。
今回のプロジェクトもそうでした。
ただ、「なぜルーティングが必要か」という本質は変わりません。
この「なぜ?」を理解しておくと、バージョンや使用するライブラリが変わっても応用が効き、実務でのトラブルシューティングや若手育成などにも活かせると感じています。

コメント

このブログの人気の投稿

ProxmoxでLet's Encryptを使用した証明書セットアップをやってみた

AIと共に「考える」エンジニアに!