React でルーティング

次は、React でルーティングします。

参考サイトは「入門者でもわかるReact Routerを利用したルーティング設定の基礎」。

プロジェクト作成

yarn で my-routing-app というプロジェクトを作成します。


yarn create react-app my-routing-app

React Routerをインストールします。


cd ~/my-routing-app
yarn add react-router-dom

起動します。


cd ~/my-routing-app
yarn start

ルーティング

App.js を編集します。

~/my-routing-app/src/App.js

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

function App() {
  return (
    <BrowserRouter>
      <h1>Hello React Router</h1>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>
    </BrowserRouter>
  );
}

function Home() {
  return <h2>Home</h2>;
}
function About() {
  return <h2>About</h2>;
}
function Contact() {
  return <h2>Contact</h2>;
}

export default App;

今回の最終型

いきなりですが、参考サイトを元に作った今回の最終型です。

多くは、chatGPT さんにお願いしました。

App.js を変更します。

~/my-routing-app/src/App.js

import React from 'react';
import { BrowserRouter, Route, Routes, NavLink, Link, useMatch, useParams } from 'react-router-dom';

const posts = [
  { id: 1, title: 'React', content: 'React Tutorial' },
  { id: 2, title: 'Vue', content: 'Vue.js Tutorial' },
  { id: 3, title: 'Laravel', content: 'Laravel Tutorail' },
];

function App() {
  return (
    <BrowserRouter>
      <header>
        <h1>Hello React Router</h1>
        <nav>
          <ul>
            <li>
              <NavLinkWithActiveStyle to="/">
                Home
              </NavLinkWithActiveStyle>
            </li>
            <li>
              <NavLinkWithActiveStyle to="/about">
                About
              </NavLinkWithActiveStyle>
            </li>
            <li>
              <NavLinkWithActiveStyle to="/contact">
                Contact
              </NavLinkWithActiveStyle>
            </li>
            <li>
              <NavLinkWithActiveStyle to="/posts">
                Posts
              </NavLinkWithActiveStyle>
            </li>
          </ul>
        </nav>
      </header>

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact />} />
        <Route path="/posts" element={<Posts />} />
        <Route path="/posts/:id" element={<Post />} />
        <Route path="*" element={<NotFound />} />
      </Routes>
    </BrowserRouter>
  );
}

function Home() {
  return <h2>Home</h2>;
}

function About() {
  return <h2>About</h2>;
}

function Contact() {
  return <h2>Contact</h2>;
}

function Posts() {
  return (
    <div>
      <h2>Post List</h2>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>
            <NavLink to={`/posts/${post.id}`}>{post.title}</NavLink>
          </li>
        ))}
      </ul>
    </div>
  );
}

function Post() {
  const { id } = useParams(); // useParams フックを使用して URL パラメータを取得

  // id を数値に変換して投稿を検索
  const post = posts.find((post) => post.id === Number(id));

  // 投稿が見つからない場合は404エラーを表示
  if (!post) {
    return <NotFound />;
  }

  return (
    <div>
      <h2>{post.title}</h2>
      <p>{post.content}</p>
    </div>
  );
}

function NotFound() {
  return <h2>404 Not Found</h2>;
}

function NavLinkWithActiveStyle({ to, children }) {
  const match = useMatch(to);
  return (
    <Link to={to} style={match ? { fontWeight: 'bold', color: 'red' } : {}}>
      {children}
    </Link>
  );
}

export default App;

リンク先は別ファイルに

これまでは、すべて App.js に書いてきましたが、当然ながら別ファイルにするだろうと思って chatGPT に質問したところ、以下のような回答がありました。

ページごとに個別のコンポーネントを作成し、それらを App.jsx でルーティングするのが一般的です。
ですので、HomeAboutContact などのページに対応する個別のコンポーネントファイルを作成し、それらを App.jsx 内でインポートしてルーティングすることが推奨されます。

当然でしょうね。

構造はこのようにしました。


src/
|-- components/
|   |-- Home.jsx
|   |-- About.jsx
|   |-- Contact.jsx
|   |-- Post.jsx
|   |-- Posts.jsx
|   |-- NavLinkWithActiveStyle.js
|   └-- NotFound.jsx
└-- App.jsx

各ファイルの設定です。

~/my-routing-app/src/componets/Home.js

import React from 'react';

function Home() {
  return (
    <div>
      <h2>Home Page</h2>
      <p>Welcome to the Home Page!</p>
    </div>
  );
}

export default Home;
~/my-routing-app/src/componets/About.js

import React from 'react';

function About() {
  return (
    <div>
      <h2>About Page</h2>
      <p>This is About Page!</p>
    </div>
  );
}

export default About;
~/my-routing-app/src/componets/Contact.js

import React from 'react';

function Contact() {
  return (
    <div>
      <h2>Contact Page</h2>
      <p>Welcom. This is contact page.</p>
    </div>
  );
}

export default Contact;
~/my-routing-app/src/componets/Posts.js

import React from 'react';
import { NavLink } from 'react-router-dom'; // NavLink をインポート

function Posts() {
  // 仮の投稿データ
  const posts = [
    { id: 1, title: 'React', content: 'React Tutorial' },
    { id: 2, title: 'Vue', content: 'Vue.js Tutorial' },
    { id: 3, title: 'Laravel', content: 'Laravel Tutorail' },
  ];

  return (
    <div>
      <h2>Post List</h2>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>
            <NavLink to={`/posts/${post.id}`}>{post.title}</NavLink>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default Posts;
~/my-routing-app/src/componets/Post.js

import React from 'react';
import { useParams } from 'react-router-dom'; // useParams をインポート

function Post() {
  const { id } = useParams(); // useParams フックを使用して URL パラメータを取得

  // 仮の投稿データ
  const posts = [
    { id: 1, title: 'React', content: 'React Tutorial' },
    { id: 2, title: 'Vue', content: 'Vue.js Tutorial' },
    { id: 3, title: 'Laravel', content: 'Laravel Tutorail' },
  ];

  // id を数値に変換して投稿を検索
  const post = posts.find((post) => post.id === Number(id));

  // 投稿が見つからない場合は404エラーを表示
  if (!post) {
    return <NotFound />;
  }

  return (
    <div>
      <h2>{post.title}</h2>
      <p>{post.content}</p>
    </div>
  );
}

export default Post;
~/my-routing-app/src/componets/NavLinkWithActiveStyle.js

import React from 'react';
import { Link, useLocation } from 'react-router-dom';

function NavLinkWithActiveStyle({ to, children }) {
  const location = useLocation();
  const isActive = location.pathname === to;

  return (
    <Link
      to={to}
      style={isActive ? { fontWeight: 'bold', color: 'red' } : {}}
    >
      {children}
    </Link>
  );
}

export default NavLinkWithActiveStyle;
~/my-routing-app/src/componets/NotFound.js

import React from 'react';

function NotFound() {
  return <h2>404 Not Found</h2>;
}

export default NotFound;

そうして、App.js の方でインポートします。

~/my-routing-app/src/App.js

import React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Home from './components/Home';
import About from './components/About';
import Contact from './components/Contact';
import Posts from './components/Posts';
import Post from './components/Post';
import NotFound from './components/NotFound';
import NavLinkWithActiveStyle from './components/NavLinkWithActiveStyle';

function App() {
  return (
    <BrowserRouter>
      <header>
        <h1>Hello React Router</h1>
        <nav>
          <ul>
            {/* NavLinkWithActiveStyle コンポーネントの使用 */}
            <li>
              <NavLinkWithActiveStyle to="/">Home</NavLinkWithActiveStyle>
            </li>
            <li>
              <NavLinkWithActiveStyle to="/about">About</NavLinkWithActiveStyle>
            </li>
            <li>
              <NavLinkWithActiveStyle to="/contact">Contact</NavLinkWithActiveStyle>
            </li>
            <li>
              <NavLinkWithActiveStyle to="/posts">Posts</NavLinkWithActiveStyle>
            </li>
          </ul>
        </nav>
      </header>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact />} />
        <Route path="/posts" element={<Posts />} />
        <Route path="/posts/:id" element={<Post />} />
        <Route path="*" element={<NotFound />} />
      </Routes>
    </BrowserRouter>
  );
}

export default App;

これはとてもわかりやすい。