カテゴリー別アーカイブ: react-rounter

SSR を利用した SPA の実装 ~ クライアントサイドルーティング の実装 ~

Overview

SPA におけるページ遷移は, (API のやりとりは別として) サーバーを介さずに, クライアントサイドのみで処理をします. このとき, URL が変更されないと, SEO 対策に不利であったり, ユーザーがそのページをブックマークできないなどの不都合が生じます.

したがって, 遷移したページに応じて URL も変更する必要があります. SPA では, この処理をクライアントサイド側で実装します.

これが, クライアントサイドルーティングです. クライアントサイドルーティングの実装には, 大きく 2 つのアプローチがあります.

  • History API を利用して自前でルーティング
  • react-router を利用する

この記事では一般的な方法である react-router (4.x) を利用した, クライアントサイドルーティングの実装方法について紹介したいと思います.

1. Install react-router

$ npm install --save react-router-dom

2. Implement Component for Routing

まずは, ルーティングを実装する前に, 前回の記事で実装した コンポーネント以外に, と コンポーネントを実装しておきます. いずれも確認のためのコンポーネントなので実装は単純です.

components/Home/index.js

import React from 'react';

const Home = () => (
<h2>Home</h2>
);

export default Home;

components/About/index.js

import React from 'react';

const About = () => (
<h2>About</h2>
);

export default About;

さて, ここから, クライアントサイドルーティングのコアとなる コンポーネントを実装します

component/Router/index.js

import React, { Component } from 'react';
import { BrowserRouter, Route, Link } from 'react-router-dom';
import Home from '../../components/Home';
import About from '../../components/About';
import Counter from '../../components/Counter';

export default class Router extends Component {
    render() {
        return (
            <BrowserRouter>
                <nav>
                    <ul>
                        <li><Link to="/>>Home</Link></li>
                        <li><Link to="/about">About</Link></li>
                        <li><Link to="/counter">Counter from 0</Link></li>
                        <li><Link to="/counter/100">Counter from 100</Link></li>
                    </ul>

                    <Route exact path="/" component={Home} />
                    <Route path="/about" component={About} />
                    <Route exact path="/counter" component={Counter} />
                    <Route path="/counter/:count" component={Counter} />
                </nav>
            </BrowserRouter>
        );
    }
}

まず, <nav> の部分ですが, <Link /> コンポーネントを使ってナビゲーションを実装する必要があります. <Link /> コンポーネントは <a /> にレンダリングされますが, 直接 <a /> タグでナビゲーションを実装しないように注意してください. <a /> タグでナビゲーションを実装してしまうと通常のページ遷移 (サーバーにリクエストを送信してレスポンスを受信してページを描画する) となってしまいます (<Link /> コンポーネントのソース). <Link /> コンポーネントの to props に記述した文字列がパスとなります.

そして, クライアントサイドルーティングを実際に担っているのが <Route /> コンポーネントです. path には, URL のパスを, component には, path で指定した URL にアクセスした場合にレンダリングするコンポーネントを指定します. また, path には, 変数を渡すことも可能で, その場合は : をつけて, :count のように指定します. このようにすることで, 対応するコンポーネントの props にその値が渡されます.

そこで, <Counter /> コンポーネントを少し改変します.

components/Counter/index.js

import React, { Component } from 'react';

export default class Counter extends Component {
    static CLASS_NAME = 'Counter';

    constructor(props) {
        super(props);

        const count = parseInt(props.match.params.count);

        this.state = {
            count : isNaN(count) ? 0 : count
        };
    }

    // ...

こうすることで, 例えば, /counter/100 にアクセスした場合, props.match.params.count に 100 が渡されるので, state の初期値は 0 ではなく, 100 になります.

exact を指定することで, / にアクセスした場合のみ <Home /> コンポーネントを描画するようにします.

exact を指定しない場合, /about や /counter にアクセスしても <Home /> コンポーネントが描画されてしまいます (もっとも, 意図的にそうするのであれば, exact を指定しないようにします).

<Route exact path="/" component={Home} />

同様の理由で, /counter にも exact を指定しています.

<Route exact path="/counter" component={Counter} />

3. Render Router Component

コンポーネントが実装できればあとはそれをレンダリングするだけです.

import React from 'react';
import ReactDOM from 'react-dom';
import Router from './components/Router';

ReactDOM.render(<Router />, document.getElementById('app'));

最後に, サーバーサードにもルーティングを追加します. そうしないと, 例えば, 直接 /about にアクセスしたときに, 404 となってしまうからです.

app-server.js

app.get('/', (req, res, next) => {
    render(req, res);
});

app.get('/about', (req, res, next) => {
    render(req, res);
});

app.get('/counter', (req, res, next) => {
    render(req, res);
});

app.get('/counter/:count', (req, res, next) => {
    render(req, res);
});