【React 入門】ルーティング(ページ遷移)の設定方法

React

今回は、React でのルーティングの設定方法を見ていきたいと思います。

react-router-dom の基本的な使い方

ここでは以下のようなサンプルを作ります。


まずは以下のコマンドでルーターをインストールします。

npm install react-router-dom


そして、Home.js About.js という2つのページをつくります。

export const Home = () => {
    return (
        <div>
            <h1>Home</h1>
        </div>
    )
}
export const About = () => {
    return (
        <div>
            <h1>About</h1>
        </div>
    )
}


そして、App.js を以下のように書きます。

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

import { Home } from "./Home";
import { About } from "./About";

function App() {
    return (
        <BrowserRouter>
            <nav>
                <Link to="/">Home</Link>
                <Link to="/about">About</Link>
            </nav>
            <Switch>
                <Route exact path="/">
                    <Home />   
                </Route>
                <Route path="/about">
                    <About />   
                </Route>
            </Switch>
        </BrowserRouter>
    );
}

export default App;


まず全体を<BrowserRouter>タグで囲みます。

<Link to=”パス”>タグがa要素として出力され、ここを押すことでページを切り替えることができます。

<Link to="/">Home</Link>
<Link to="/about">About</Link>


そして、<Switch>タグの中が実際にページとして描画する領域となり、
どのパスのときに描画するのかを<Router path=”パス”>で指定します。

ただ、デフォルトでは指定したパスの配下全てに適用されてしまうため、
そのパスだけに適用したいときは exact を指定します。

<Route exact path="/">
    <Home />   
</Route>

ネストされたルーティング

たとえば「/about」というルートの下に「/about/detail/A」や「/about/detail/B」がある場合のルーティングをみていきます。



まずは AboutDetailA.js AboutDetailB.js をつくります。

export const AboutDetailA = () => {
    return (
        <div>
            <h1>About-Detail-A</h1>
        </div>
    )
}
export const AboutDetailB = () => {
    return (
        <div>
            <h1>About-Detail-B</h1>
        </div>
    )
}


そして About.js を修正します。

import { Link } from 'react-router-dom'

export const About = () => {
    return (
        <div>
            <h1>About</h1>
            <nav>
                <Link to="/about/detail/A">Detail-A</Link>
                <Link to="/about/detail/B">Detail-B</Link>
            </nav>
        </div>
    )
}


では App.js を書き換えます。
ルーティングが階層構造になっているのがわかるかと思います。

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

import { Home } from "./Home";
import { About } from "./About";
import { AboutDetailA } from './AboutDetailA';
import { AboutDetailB } from './AboutDetailB';

function App() {
    return (
        <BrowserRouter>
            <nav>
                <Link to="/">Home</Link>
                <Link to="/about">About</Link>
            </nav>
            <Switch>
                <Route exact path="/">
                    <Home />   
                </Route>
                <Route path="/about" render={({ match: { url } }) => (
                    <Switch>
                        <Route exact path={url}>
                            <About />
                        </Route>
                        <Route path={`${url}/detail/A`}>
                            <AboutDetailA />
                        </Route>
                        <Route path={`${url}/detail/B`}>
                            <AboutDetailB />
                        </Route>
                    </Switch>
                )} />
            </Switch>
        </BrowserRouter>
    );
}

export default App;



以下の部分ですが、render に引数を与えることでプロパティを付与することができます。

<Route path="パス" render={(props) => (
    ...
)} />


たとえば path=”” で指定したパスを配下のルートに渡したい場合、props.match.url で渡すことができますが、
これを分割代入で表すと { match: { url } } になります。


したがって、以下のように書くことで url という変数に「/about」を渡すことができます。

<Route path="/about" render={({ match: { url } }) => (
    <Switch>
        <Route exact path={url}>
            <About />
        </Route>
        <Route path={`${url}/detail/A`}>
            <AboutDetailA />
        </Route>
        <Route path={`${url}/detail/B`}>
            <AboutDetailB />
        </Route>
    </Switch>
)} />

ルーティングの切り出し

これまでルーティングの記述を App.js に書いていましたが、
別のファイルに定義して管理を分けようと思います。

では、「/src」の中に「router」ディレクトリをつくり、
その中に「Router.js」を作ります。


そして App.js を以下のように修正します。

import { BrowserRouter, Link } from 'react-router-dom'
import { Router } from './router/Router'

function App() {
    return (
        <BrowserRouter>
            <nav>
                <Link to="/">Home</Link>
                <Link to="/about">About</Link>
            </nav>
            <Router />
        </BrowserRouter>
    );
}

export default App;


<switch>タグで囲んでいた部分を、丸々<Router />に置き換えることでスッキリしました。

ではその置き換えた部分をRouter.js に移します。

import { Switch, Route } from 'react-router-dom'
import { About } from '../About'
import { AboutDetailA } from '../AboutDetailA'
import { AboutDetailB } from '../AboutDetailB'
import { Home } from '../Home'

export const Router = () => {
    return (
        <Switch>
            <Route exact path="/">
                <Home />   
            </Route>
            <Route path="/about" render={({ match: { url } }) => (
                <Switch>
                    <Route exact path={url}>
                        <About />
                    </Route>
                    <Route path={`${url}/detail/A`}>
                        <AboutDetailA />
                    </Route>
                    <Route path={`${url}/detail/B`}>
                        <AboutDetailB />
                    </Route>
                </Switch>
            )} />
        </Switch>
    )
}


次に、About ページのルートを他のファイルに配列として定義し、それを読み込んでループさせたいと思います。

では「/stc/router」内に「AboutRoutes.js」をつくり、以下のように書きます。

import { About } from "../About";
import { AboutDetailA } from "../AboutDetailA";
import { AboutDetailB } from "../AboutDetailB";

export const AboutRoutes = [
    {
        path: '/',
        exact: true,
        children: <About />
    },
    {
        path: '/detail/A',
        exact: false,
        children: <AboutDetailA />
    },
    {
        path: '/detail/B',
        exact: false,
        children: <AboutDetailB />
    },
]



そして、Router.js を修正します。

import { Switch, Route } from 'react-router-dom'
import { Home } from '../Home'
import { AboutRoutes } from './AboutRoutes'

export const Router = () => {
    return (
        <Switch>
            <Route exact path="/">
                <Home />   
            </Route>
            <Route 
                path="/about" 
                render={({ match: { url } }) => (
                    <Switch>
                        {AboutRoutes.map((route) => (
                            <Route 
                                key={route.path} 
                                exact={route.exact} 
                                path={url + route.path}
                            >
                                {route.children}
                            </Route>
                        ))}
                    </Switch>
                )} 
            />
        </Switch>
    )
}


以下の部分についてですが、

{AboutRoutes.map((route) => (
    <Route 
        key={route.path} 
        exact={route.exact} 
        path={url + route.path}
    >
        {route.children}
    </Route>
))}


map関数を使い、配列をループさせています。
その際、key というプロパティで一意となる値を設定する必要があるため、ここではパスを指定しています。

URLパラメータの使用

これまで「/about」の下層ページとして 「/detail/A」「/detail/B」をつくり、
それぞれのルーティングやコンポーネントを手動で設定しましたが、
この A B … を id というパラメータとして扱い、動的に表示できるようにしていきます。



では、About.js を修正し、検証用にルートを3つ用意します。

import { Link } from 'react-router-dom'

export const About = () => {
    return (
        <div>
            <h1>About</h1>
            <nav>
                <Link to="/about/detail/A">Detail-A</Link>
                <Link to="/about/detail/B">Detail-B</Link>
                <Link to="/about/detail/C">Detail-C</Link>
            </nav>
        </div>
    )
}


Router.js は特に変更はありませんが、改めて載せておきます。

import { Switch, Route } from 'react-router-dom'
import { Home } from '../Home'
import { AboutRoutes } from './AboutRoutes'

export const Router = () => {
    return (
        <Switch>
            <Route exact path="/">
                <Home />   
            </Route>
            <Route 
                path="/about" 
                render={({ match: { url } }) => (
                    <Switch>
                        {AboutRoutes.map((route) => (
                            <Route 
                                key={route.path} 
                                exact={route.exact} 
                                path={url + route.path}
                            >
                                {route.children}
                            </Route>
                        ))}
                    </Switch>
                )} 
            />
        </Switch>
    )
}


続いて AboutRoutes.js を修正します。

import { About } from "../About";
import { AboutDetail } from "../AboutDetail";

export const AboutRoutes = [
    {
        path: '/',
        exact: true,
        children: <About />
    },
    {
        path: '/detail/:id',
        exact: false,
        children: <AboutDetail />
    }
]


以下のように、「:パラメータ名」とすることでパラメータを動的に扱うことができます。

{
    path: '/detail/:id',
    exact: false,
    children: <AboutDetail />
}


そして、これまで AboutDetailA.js AboutDetailB.js の2つ作りましたが、
片方を削除して残りを AboutDetail.js とし、以下のように修正します。

import { useParams } from 'react-router-dom'

export const AboutDetail = () => {
    const { id } = useParams()

    return (
        <div>
            <h1>About-Detail-{id}</h1>
        </div>
    )
}


まず、ルートのパラメータを使用するため useParams をインポートします。

そして、{ パラメータ名 } = useParams() とすることで変数として使用することができます。

クエリパラメータの使用

次はクエリパラメータの使用をみていきます。

例として、「/about/detail/C」に「page=6」というパラメータを与えてみます。


まずは About.js を以下のように書きます。

import { Link } from 'react-router-dom'

export const About = () => {
    return (
        <div>
            <h1>About</h1>
            <nav>
                <Link to="/about/detail/A">Detail-A</Link>
                <Link to="/about/detail/B">Detail-B</Link>
                <Link to="/about/detail/C?page=6">Detail-C</Link>
            </nav>
        </div>
    )
}


そして、AboutDetail.js を修正します。

import { useParams, useLocation } from 'react-router-dom'

export const AboutDetail = () => {
    const { id } = useParams()
    const { search } = useLocation()
    const query = new URLSearchParams(search)

    return (
        <div>
            <h1>About-Detail-{id}</h1>
            <p>{query.get('page')}ページ目</p>
        </div>
    )
}


まず、useLocation をインポートし、以下のようにクエリパラメータを取り出します。

const { search } = useLocation()
const query = new URLSearchParams(search)


そして、query.get(‘パラメータ名’) とすることで取得することができます。

ページ間のデータの受け渡し

ページ間でデータを受け渡す方法をみていきます。

例では、About.js から AboutDetail.js に msg という変数を渡してみます。


では About.js を以下のように書きます。

import { Link } from 'react-router-dom'

export const About = () => {
    const msg = '詳細ページです'
    return (
        <div>
            <h1>About</h1>
            <nav>
                <Link to={{ pathname:"/about/detail/A", state: msg }}>Detail-A</Link>
                <Link to={{ pathname:"/about/detail/B", state: msg }}>Detail-B</Link>
                <Link to={{ pathname:"/about/detail/C", state: msg }}>Detail-C</Link>
            </nav>
        </div>
    )
}


ご覧のように、Link タグの to 属性を以下のようなオブジェクトで記述します。

{ pathname:"パス", state: 渡すデータ }


そして AboutDetail.js を以下のように修正します。

import { useParams, useLocation } from 'react-router-dom'

export const AboutDetail = () => {
    const { id } = useParams()
    const { state } = useLocation()

    return (
        <div>
            <h1>About-Detail-{id}</h1>
            <p>{state}</p>
        </div>
    )
}


useLocation() から state を取り出し、変数として使うことができます。

const { state } = useLocation()
.
.
.
<p>{state}</p>

useHistory を使用したページ遷移

Link タグ以外でページ遷移する方法をみていきます。

例では、button 要素のクリックでページ遷移させたいと思います。


まずは About.js を以下のように書きます。

import { Link, useHistory } from 'react-router-dom'

export const About = () => {
    const history = useHistory()
    const goToDetailA = () => {
        history.push('/about/detail/A')
    }

    return (
        <div>
            <h1>About</h1>
            <nav>
                <Link to="/about/detail/A">Detail-A</Link>
                <Link to="/about/detail/B">Detail-B</Link>
                <Link to="/about/detail/C">Detail-C</Link>
            </nav>
            <button onClick={goToDetailA}>Detail-Aへ</button>
        </div>
    )
}


まず、インポートした useHistory() を変数(history)に入れ、
history.push(‘遷移先のパス’)
を実行することでページ遷移することができます。

次に AboutDetail.js を修正します。

import { useParams, useHistory } from 'react-router-dom'

export const AboutDetail = () => {
    const { id } = useParams()
    const history = useHistory()
    const goBack = () => {
        history.goBack()
    }

    return (
        <div>
            <h1>About-Detail-{id}</h1>
            <button onClick={goBack}>戻る</button>
        </div>
    )
}


前のページに戻る場合は、
history.goBack()
を実行します。

404ページ

存在しないページにアクセスしたときに、404ページ(ページが見つかりません等のエラーを表示するページ)に遷移させる方法をみていきます。

まずは404ページを作ります。

import { Link } from 'react-router-dom'

export const Page404 = () => {
    return (
        <div>
            <h1>ページが見つかりません</h1>
            <Link to="/">Homeへ</Link>
        </div>
    )
}


続いて Router.js を修正します。

import { Switch, Route } from 'react-router-dom'
import { Home } from '../Home'
import { Page404 } from '../Page404'
import { AboutRoutes } from './AboutRoutes'

export const Router = () => {
    return (
        <Switch>
            <Route exact path="/">
                <Home />   
            </Route>
            <Route 
                path="/about" 
                render={({ match: { url } }) => (
                    <Switch>
                        {AboutRoutes.map((route) => (
                            <Route 
                                key={route.path} 
                                exact={route.exact} 
                                path={url + route.path}
                            >
                                {route.children}
                            </Route>
                        ))}
                    </Switch>
                )} 
            />
            <Route path="*">
                <Page404 />
            </Route>
        </Switch>
    )
}


一番最後に path=”*” を指定したルートを追加することで、
どのルートにも当てはまらなかった場合のページを表示させることができます。

<Route path="*">
    <Page404 />
</Route>


適当に「/xxx」などルーティング設定していないパスにアクセスすると、以下のような表示になります。





今回は以上になります。
ご覧いただきありがとうございました!

続きはこちら↓

コメント

コンタクトフォーム

    タイトルとURLをコピーしました