React: React Router v6 でルーティングする step2
2022年01月28日 Windows11での情報です。
前回に続いて、React Router v6 でのルーティングについて、見ていきます。
前回はこちら
React: React Router v6 でルーティングする step1
環境
- vite: v2.7.2
- node: v16.13.2
- react: v17.0.2
- typescript: v4.4.4
- react-router-dom: v6.2.1
前回の記事でのコンポーネントの状態
SamplePage1は、シンプルなコンポーネントです。
import React from "react";
export const SamplePage1:React.VFC =() => {
return <h3>Sample Page 1</h3>
}
SamplePage2は、クエリーパラメータを使用したルーティング用のコンポーネントです。
import React from "react";
import { useSearchParams } from "react-router-dom";
export const SamplePage2:React.VFC =() => {
const [searchParams] = useSearchParams();
const query1 = searchParams.get("query1");
const query2 = searchParams.get("query2");
return (
<>
<p>query1={query1}</p>
<p>query2={query2}</p>
</>
);
}
SamplePage3は、propsを使用したルーティング用のコンポーネントです。
import React from "react";
type Props = {
Message: string
};
export const SamplePage3:React.VFC<Props> = (props) => {
return <p>{props.Message}</p>
}
NotFoundは、一致するURLがないルーティング用のコンポーネントです。
import React from "react";
export const NotFound = () => {
return (
<>
<h1>404</h1>
<h3>お探しのページは見つかりませんでした。</h3>
</>
);
}
RouterConfigは、ルートを定義したコンポーネントです。
import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { SampleHome } from "./components/SampleHome";
import { SamplePage1 } from "./components/SamplePage1";
import { SamplePage2 } from "./components/SamplePage2";
import { SamplePage3 } from "./components/SamplePage3";
import { NotFound } from "./components/NotFound";
export const RouterConfig:React.VFC =() => {
return (
<>
<BrowserRouter>
<Routes>
<Route index element={<SampleHome />} />
<Route path="page1" element={<SamplePage1 />} />
<Route path="page2" element={<SamplePage2 />} />
<Route path="page3_hello" element={<SamplePage3 Message="Hello Router" />} />
<Route path="page3_hi" element={<SamplePage3 Message="Hi Router"/>} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
</>
);
}
main.tsxでRouterConfigコンポーネントを読み込みます。
※ビルドツールviteでReactプロジェクトを作成するとmain.tsxが一番最初のコンポーネントになりますが、create-react-appでReactプロジェクトを作成すると、一番最初のコンポーネントはindex.tsxになります。(適当に読み替えてください)
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import { RouterConfig } from "./RouterConfig";
ReactDOM.render(
<React.StrictMode>
<RouterConfig />
</React.StrictMode>,
document.getElementById('root')
)
ボタンクリックでルーティングする
今までのサンプルでは、リンクでルーティングしてきました。
ボタンのクリックなどでルーティングを行う場合は、useNavigateフックが返すnavigate関数を使用します。
SampleHomeコンポーネントで「Sample Page1」ボタンをクリックすると、SamplePage1にルーティングするように追記します。
import React from "react";
import { Link } from "react-router-dom";
import { createSearchParams } from "react-router-dom";
import { RouterConfig } from "../RouterConfig";
import { useNavigate } from 'react-router-dom';
export const SampleHome:React.VFC =() => {
const navigate = useNavigate();
const params: string = createSearchParams({
query1: "value3",
query2: "value4"
}).toString();
return (
<>
<h1>Sample Home</h1>
<nav>
<ul>
<li><Link to="page1">Sample Page1</Link></li>
<li><Link to="page2">Sample Page2</Link></li>
<li><Link to="page2?query1=value1&query2=value2">Sample Page2 With Query1</Link></li>
<li><Link to={`page2?${params}`}>Sample Page2 With Query2</Link></li>
<li><Link to="page3_hello">Sample Page3 Hello</Link></li>
<li><Link to="page3_hi">Sample Page3 Hi</Link></li>
</ul>
<button onClick={() => navigate("page1")}>Sample Page1</button>
</nav>
</>
);
}
動作を確認してみます。
ネストされたページを表示する
次はネストされたページを表示する方法です。
「ネストされたページってなに?」ってことで、コードを書く前に、先に動作を確認します。
まずは前回作成したものから確認してみます。
page1リンクをクリックすると、page全体が変更されます。
今回作成するものです。
「Sample Page4」ページで「Show Child1」リンク、「Show Child2」リンクをクリックすると、それぞれのページの内容が、Sample Page4ページ内に表示されます。
今回新たに追加したSamplePage4コンポーネント、SamplePage4Child1、SamplePage4Child2です。
注目は<Outlet />
です。
RouterConfigで定義している「ネストされたルート」のコンポーネントを<Outlet />
部分にレンダリングします。
「Clear」ボタンでは、「""」を指定してルーティングしています。
import React, { VFC } from "react";
import { Outlet, Link, useNavigate} from "react-router-dom";
export const SamplePage4:VFC = () => {
const navigate = useNavigate();
return (
<>
<h3>Sample Page 4</h3>
<ul>
<li><Link to="child1">Show Child1</Link></li>
<li><Link to="child2">Show Child2</Link></li>
</ul>
<button onClick={()=> navigate("") }>clear</button>
<Outlet />
</>
);
}
export const SamplePage4Child1:VFC = () => {
return <h3>Sample Page 4 Child1</h3>;
}
export const SamplePage4Child2:VFC = () => {
return <h3>Sample Page 4 Child2</h3>;
}
ルートを定義したRouterConfigコンポーネントです。
ルート「path="page4"」の定義に、ネストしたルート「index」と「path="child1"」と「path="child2"」を指定しています。
「index」ではelementを指定していないので、SamplePage4コンポーネントの<Outlet />
には何も表示されません。
SamplePage4の「Clear」ボタンのnavigate("")は、この「index」にルーティングされます。
import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { SampleHome } from "./components/SampleHome";
import { SamplePage1 } from "./components/SamplePage1";
import { SamplePage2 } from "./components/SamplePage2";
import { SamplePage3 } from "./components/SamplePage3";
import { SamplePage4, SamplePage4Child1, SamplePage4Child2 } from "./components/SamplePage4";
import { NotFound } from "./components/NotFound";
export const RouterConfig:React.VFC =() => {
return (
<>
<BrowserRouter>
<Routes>
<Route index element={<SampleHome />} />
<Route path="page1" element={<SamplePage1 />} />
<Route path="page2" element={<SamplePage2 />} />
<Route path="page3_hello" element={<SamplePage3 Message="Hello Router" />} />
<Route path="page3_hi" element={<SamplePage3 Message="Hi Router"/>} />
<Route path="page4" element={<SamplePage4 />} >
<Route index />
<Route path="child1" element={<SamplePage4Child1 />} />
<Route path="child2" element={<SamplePage4Child2 />} />
</Route>
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
</>
);
}
ダイナミックルート
続いてはダイナミックルートです。
SamplePage4コンポーネントでは「Link to="123"」とルートを指定しています。
これはルートを定義したRouterConfigコンポーネントの「path=":cildid"」ルーティングされます。
新しくSamplePage4Child3コンポーネントを作成します。
useParamsフックを使用して、「cildid」パラメータを取得します。
import React, { VFC } from "react";
import { Outlet, Link, useNavigate, useParams, NavigateFunction} from "react-router-dom";
export const SamplePage4:VFC = () => {
const navigate:NavigateFunction = useNavigate();
return (
<>
<h3>Sample Page 4</h3>
<ul>
<li><Link to="child1">Show Child1</Link></li>
<li><Link to="child2">Show Child2</Link></li>
<li><Link to="123">Show Child3</Link></li>
</ul>
<button onClick={()=> navigate("") }>clear</button>
<Outlet />
</>
);
}
export const SamplePage4Child1:VFC = () => {
return <h3>Sample Page 4 Child1</h3>;
}
export const SamplePage4Child2:VFC = () => {
return <h3>Sample Page 4 Child2</h3>;
}
export const SamplePage4Child3:VFC = () => {
type Param = {
cildid?: string
}
const params:Param = useParams<Param>();
return (
<>
<h3>Sample Page 4 Child3</h3>
<p>{`cildid=${params?.cildid}`}</p>
</>
);
}
RouterConfigコンポーネントでは「path=":cildid"」としてダイナミックルートを定義しています。
import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { SampleHome } from "./components/SampleHome";
import { SamplePage1 } from "./components/SamplePage1";
import { SamplePage2 } from "./components/SamplePage2";
import { SamplePage3 } from "./components/SamplePage3";
import { SamplePage4, SamplePage4Child1, SamplePage4Child2, SamplePage4Child3 } from "./components/SamplePage4";
import { NotFound } from "./components/NotFound";
export const RouterConfig:React.VFC =() => {
return (
<>
<BrowserRouter>
<Routes>
<Route index element={<SampleHome />} />
<Route path="page1" element={<SamplePage1 />} />
<Route path="page2" element={<SamplePage2 />} />
<Route path="page3_hello" element={<SamplePage3 Message="Hello Router" />} />
<Route path="page3_hi" element={<SamplePage3 Message="Hi Router"/>} />
<Route path="page4" element={<SamplePage4 />} >
<Route index />
<Route path="child1" element={<SamplePage4Child1 />} />
<Route path="child2" element={<SamplePage4Child2 />} />
<Route path=":cildid" element={<SamplePage4Child3 />} />
</Route>
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
</>
);
}
動作を確認してみます。
アクセスしているページのリンクを装飾する
最後にアクセスしているページのリンクを装飾してみます。
アクセスされているページのリンクを装飾するには、<Link>
の代わりに<NavLink>
を使用します。
<NavLink>
は設定されたリンクが現在アクセス中であれば、isActive
プロパティにtrueが設定されます。
isActive
プロパティの値に応じて style か className のどちらかにスタイルやクラス文字列を設定することで、アクセスされているページのリンクを装飾します。
import React, { VFC } from "react";
import { Outlet, NavLink, useNavigate, useParams, NavigateFunction} from "react-router-dom";
export const SamplePage4:VFC = () => {
const active = {
fontWeight: "bold",
color: "#d57276"
}
const inactive = {
fontWeight: "normal",
color: "#65b2c6"
}
const navigate:NavigateFunction = useNavigate();
const linkStyle = (isActive:boolean) => {
return isActive ? active : inactive;
}
return (
<>
<h3>Sample Page 4</h3>
<ul>
<li><NavLink to="child1" style={({isActive}) => linkStyle(isActive)}>Show Child1</NavLink></li>
<li><NavLink to="child2" style={({isActive}) => linkStyle(isActive)}>Show Child2</NavLink></li>
<li><NavLink to="123" style={({isActive}) => linkStyle(isActive)}>Show Child3</NavLink></li>
</ul>
<button onClick={()=> navigate("") }>clear</button>
<Outlet />
</>
);
}
export const SamplePage4Child1:VFC = () => {
return <h3>Sample Page 4 Child1</h3>;
}
export const SamplePage4Child2:VFC = () => {
return <h3>Sample Page 4 Child2</h3>;
}
export const SamplePage4Child3:VFC = () => {
type Param = {
cildid?: string
}
const params:Param = useParams<Param>();
return (
<>
<h3>Sample Page 4 Child3</h3>
<p>{`cildid=${params?.cildid}`}</p>
</>
);
}
動作を確認してみます。
まとめ
2回にわたってReact Router v6を使用したルーティングについて、記事にまとめてみました。
簡単に使えてとても便利ですね。
Discussion