準備
TypeScript版のCreate React App を使用します。
下記でプロジェクトディレクトリを作成。
$ yarn create react-app react-router-test --template typescript $ cd react-router-test
続いてReact Routerライブラリのインストール。
$ yarn add react-router-dom@6
今回使用するバージョンはそれぞれこんな感じです。
typescript: 4.4.2
react: 18.1.0
react-router-dom: 6
一番シンプルなルーティング
最初に単純にページ(コンポーネント)の表示を切り替えるだけのルーティング設定をしてみます。
表示されるページ、ホームページと記事一覧ページコンポーネントを作ります。
src/pages/Home.tsx
import React from 'react' const Home: React.FC = () => { return ( <div> <h1>ホーム</h1> </div> ) } export default Home
src/pages/Posts.tsx
import React from 'react' const Posts: React.FC = () => { return ( <div> <h1>記事一覧</h1> </div> ) } export default Posts
この二つのページを切り替えられるようにルーターの設定をします。
App.tsx
を次のように編集します。
src/App.tsx
import React from 'react' import { BrowserRouter, Routes, Route, } from 'react-router-dom' import Home from './pages/Home' import Posts from './pages/Posts' const App: React.FC = () => { return ( <BrowserRouter> <Routes> <Route path="/" element={<Home />} /> <Route path="posts" element={<Posts />} /> </Routes> </BrowserRouter> ) } export default App
Route
のpath
にアクセスしたパス、element
に表示させるコンポーネントを指定します。
ブラウザで「/」「/posts」にアクセスしてそれぞれのコンポーネントが表示されることを確認してください。
パス | コンポーネント |
---|---|
/ | Homeコンポーネント |
/posts | Postsコンポーネント |
次はリンクを追加してそれぞれのページに遷移できるようにしてみましょう。
src/pages/Home.tsx
import React from 'react' import { Link } from 'react-router-dom' const Home: React.FC = () => { return ( <div> <ul> <li><Link to="/">ホーム</Link></li> <li><Link to="/posts">記事一覧</Link></li> </ul> <h1>ホーム</h1> </div> ) } export default Home
Postsコンポーネントにも同じように修正してください。
Link
コンポーネントのto
にroutes
で設定したパスを指定することで、指定したコンポーネントに遷移します。
aタグではなくLink
を使用することでブラウザの遷移が発生しなくなり高速で切り替えることができます。
アウトレット(outlet)機能で共通コンポーネントの設定
ナビゲーションは全ページで共通で使いたいですね。
レウアウトコンポーネントを作って、それを各コンポーネントで配置するという方法もあると思いますが、ここではアウトレット(outlet)という機能を使用してみます。
最初にレイアウト用のコンポーネントを作成します。
先ほど作成したリンクナビゲーションはここに配置します。
その下にOutlet
を配置します。最終的にはここに各ページ用のコンポーネントが表示されます。
ちなみに共通で使用するナビゲーションはlinkではなくNavLinkを使用することで現在のページにactive
クラスを付けてくれます。
src/components/Layout.tsx
import React from 'react' import { NavLink, Outlet } from 'react-router-dom' const Layout: React.FC = () => { return ( <div> <ul> <li><NavLink to="/">ホーム</NavLink></li> <li><NavLink to="/posts">記事一覧</NavLink></li> </ul> <Outlet /> </div> ) } export default Layout
アウトレットに表示されるようにルーターを次のように編集します。
src/App.tsx
<Routes> <Route path="/" element={<Layout />}> <Route index element={<Home />} /> <Route path="posts" element={<Posts />} /> </Route> </Routes>
アウトレットに表示させたいコンポーネントを子に配置するだけです。
各ページコンポーネントではナビゲーションが必要なくなったので削除しておきましょう。
src/pages/Home.tsx
import React from 'react' const Home: React.FC = () => { return ( <div> <h1>ホーム</h1> </div> ) } export default Home
Postsコンポーネントも同じように編集してください。
これで同様の結果になります。各ページコンポーネントがすっきりしましたね。
下層ページのルート設定
次に記事一覧の下に記事詳細ページを作ってみます。
記事詳細はURLからIDを取得して一つのページコンポーネントを動的に切り替える想定です。
ルーターから編集しましょう。
src/App.tsx
<Routes> <Route path="/" element={<Layout />}> <Route index element={<Home />} /> <Route path="posts" element={<Posts />} /> <Route path="posts/:postId" element={<Post />} /> </Route> </Routes>
新しくPostコンポーネントを項目を追加してます。
詳細ページURLからパラメータを取得するのでパスには:postId
とコロンをつけてキーワードを指定します。
これで「posts/1」「posts/2」にアクセスしたとき、postIdというパラメータで「1,2」が取れるようになります。
今回は詳細ページには共通でリンクを表示しないのでアウトレットは使用しないで、ルーターは並列に設定します。もし表示したい場合は入れ子にしてください。
次に記事詳細ページのPostコンポーネントを作ります。
ここでは受け取ったIDをそのまま表示してみます。
パラメータはuseParams
で取得することができます。
src/pages/Post.tsx
import React from 'react' import { useParams } from 'react-router-dom' const Post: React.FC = () => { const { postId } = useParams() return ( <div> <h2>投稿詳細</h2> <p>ID: { postId }</p> </div> ) } export default Post
詳細ページの準備ができたので、一覧ページから遷移できるようにリンクを追加しましょう。
src/pages/Posts.tsx
const Posts: React.FC = () => { return ( <div> <h1>記事一覧</h1> <ul> <li><Link to="/posts/1">記事1</Link></li> <li><Link to="/posts/2">記事2</Link></li> <li><Link to="/posts/3">記事3</Link></li> </ul> </div> ) }
これで各詳細ページへ遷移できるようになります。
クエリパラメータの取得
絞り込み検索を行う場合は、URLからクエリパラメータを取得したいですね。
その場合はuseSearchParams
というフックを使用します。
たとえば?title=hoge
という値を取りたい場合は次のようにします。
const [searchParams] = useSearchParams() console.log(searchParams.get('itle'))
遅延ロード
規模が大きくなるとWebpackなどでビルドしたファイルサイズが肥大化し初回の読み込み時間が長くなるという問題があります。
ReactのSuspenseを組み込むことで遅延ロードができます。
src/App.tsx
import React, { lazy, Suspense } from 'react' import { BrowserRouter, Routes, Route } from 'react-router-dom' import Layout from './components/Layout' const Home = lazy(() => import('./pages/Home')) const Posts = lazy(() => import('./pages/Posts')) const Post = lazy(() => import('./pages/Post')) const App: React.FC = () => { return ( <BrowserRouter> <Routes> <Route path="/" element={<Layout />}> <Route index element={ <Suspense fallback={<p>Loading...</p>}> <Home /> </Suspense> } /> <Route path="posts" element={ <Suspense fallback={<p>Loading...</p>}> <Posts /> </Suspense> } /> <Route path="posts/:postId" element={ <Suspense fallback={<p>Loading...</p>}> <Post /> </Suspense> } /> </Route> </Routes> </BrowserRouter> ) } export default App
ルーティングをオブジェクト形式で設定
今までDOMでルーティングの設定をしましたが、オブジェクト形式でも設定することができます。
新しくルーティング用ファイルを作成して設定してみましょう。
src/routes.tsx
import type { RouteObject } from 'react-router-dom' import Layout from './components/Layout' import Home from './pages/Home' import Posts from './pages/Posts' import Post from './pages/Post' import NotFound from './pages/NotFound' export const routes: RouteObject[] = [ { path: '/', element: <Layout />, children: [ { index: true, element: <Home /> }, { path: '/posts', element: <Posts /> }, { path: '/posts/:postId', element: <Post /> }, { path: '*', element: <NotFound /> } ] } ]
作成したroutes.tsx
をApp.tsx
に配置します。
src/routes.tsx
import React from 'react' import { useRoutes } from 'react-router-dom' import { routes } from './routes' const App: React.FC = () => { const router = useRoutes(routes) return ( <>{router}</> ) } export default App
BrowserRouter
はindex.tsx
に配置します。
src/index.tsx
import React from 'react' import ReactDOM from 'react-dom/client' import App from './App' import reportWebVitals from './reportWebVitals' import { BrowserRouter } from 'react-router-dom' const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ) root.render( <React.StrictMode> <BrowserRouter> <App /> </BrowserRouter> </React.StrictMode> ) reportWebVitals()
React Routerにはその他にもいろいろな機能があります。
詳しくは下記ドキュメントをご確認ください。
React Router | Docs Home