🔗

【React × TypeScript】react-router-dom 基本編

2024/03/20に公開

概要

勉強のためにReactとTypeScriptを使用したアプリを開発中です。その過程でreact-router-dom v6について調べたことをまとめています。
まだまだ勉強中なので、間違っている部分や誤解を招きやすい部分があれば教えていただけると嬉しいです。

環境構築

まずはインストールしましょう。

npm install react-router-dom @types/react-router-dom

TypeScriptのバージョンや環境によって@types/react-router-domのインストールは不要の場合もあります。

ルーティング

ページ上部に常時ヘッダーを表示させながら、ページ下部の表示のみをクリックするメニューによって変えるウェブサイト例に説明していきます。

App.tsx
import { BrowserRouter as Router, Route, Routes, Outlet } from "react-router-dom"


function App() {
  return (
    <Router>
      <Routes>
        <Route
          element={
            <div>
              <h1>Layout</h1>
              <Outlet />
            </div>
          }
        >
          <Route path="/" element={<h2>Home</h2>} />
          <Route path="/about" element={<h2>About</h2>} />
        </Route>
      </Routes>
    </Router>
  )
}

export default App

基本、Router > Routes > Route がワンセットでRouteタグで詳しい設定を行います。モジュールをインポートするのも忘れないようにしましょう。
ここでは、一番上のRouteが親となって、2つの子Routeを囲んでいます。このようにすると、親Route(Layout)は常に表示され、アクセスするパスによってOutlet部分で表示される子RouteがHomeになったりAboutになったりします。/にアクセスする場合はHomeが呼び出され、/aboutにアクセスするとAboutが呼びされる、ということです。

path:URLを設定する
element:呼び出すコンポーネントを指定する

一番外側のRouterはrootファイルに書く方法もあります。

main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import { BrowserRouter as Router } from 'react-router-dom'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <Router>
      <App />
    </Router>
  </React.StrictMode>,
)

LayoutとOutlet

先ほどのLayoutとOutletについて、もう少し見ていきます。

上の例ではコンポーネントファイルを使わないコードを紹介しましたが、実際によく見かけるのは下のようなコードだと思います。

App.tsx
import { BrowserRouter as Router, Route, Routes } from "react-router-dom"
import Layout from "./Layout"
import Home from "./home"
import About from "./About"

function App() {
  return (
    <Router>
      <Routes>
        <Route path='/' element={<Layout />}>
          <Route index element={<Home />} />
          <Route path="/about" element={<About />} />
        </Route>
      </Routes>
    </Router>
  )
}

export default App
Layout.tsx
import { Outlet } from "react-router-dom"
import Header from "./Header"

const Layout = () => {
  return (
    <>
      <Header />
      <Outlet />
    </>
  )
}

export default Layout
Header.tsx
const Header = () => {
  return (
    <h2>Header</h2>
  )
}

export default Header

図にするとこんな感じです

ここでは、Layout, Header, Home, Aboutのコンポーネントファイルを作成しました。
/にアクセスするとLayoutコンポーネントが表示され、その中にはHeaderとOutletが入っています。そうすることで、いつでもHeaderコンポーネントとOutletが表示されます。先ほども説明した通り、Outletは親Routeが囲っている子Routeをパスに合わせて表示するものです。これで、いつでもHeaderを表示しながらアクセスするパスによって下部の表示だけを変えられるようになりました。

今のままでは、URLを直接変えないと表示が変わらないので画面上にもリンクを設置していきます。

Header.tsx
import { Link } from "react-router-dom"

const Header = () => {
  return (
    <>
      <Link to='/'>Home</Link>
      <Link to='/about'>About</Link>
    </>
  )
}

export default Header

このときに使うのがLinkです。Link要素は、クリックすることで指定したコンポーネントの表示をしてa hrefのような働きをします。ただ、aタグとは違い、ページのリロードが起こらないので不要なページのリロードやを避け、stateの状態を保持してくれます。

以上で、ヘッダーは常に上部に置いたまま、その下の部分のみ表示を変更する仕様が完成です。

SwitchとRoutes

react-router-domについて検索するとSwitchを使用した例をよく見かけますが、うまく動きませんでした。v5ではSwitchを使用していたようですが、v6ではRoutesを使うのが推奨されています。

NotFoundページの作成

App.tsx
<Route path="*" element={<NotFound />} />

このコードで、存在しないパスへのアクセスは全てNotFoundページに遷移されるようになります。

参考

https://reactrouter.com/en/main

Discussion