React でルーティングをする react-router を試しました。

インストール

Browserify + Babel でコンパイルするのでそのツールを React 、 react-router と一緒にインストールしておきます。

$ npm install -g browserify
$ npm install -D babelify babel-preset-es2015 babel-preset-react
$ npm install -S react react-dom react-router

Hello, World!

とりあえず画面になにか表示するところからやってみます。

import React, { Component } from 'react';
import { render } from 'react-dom';
import { Router, Route, hashHistory } from 'react-router';

class App extends Component {
    render() {
        return (
            <div>
                Hello, react-router!
            </div>
        );
    }
}

render((
    <Router history={hashHistory}>
        <Route path="/" component={App} />
    </Router>
), document.querySelector('#app'));

Router コンポーネントと Route コンポーネントを使います。 historyhashHistory を入れると URLの # 以降を使ってルーティングをします。 browserHistory を使うと URL 全体を使うみたいです。

Route に URL のパターンと表示するコンポーネントを指定します。この場合ページを開くと App コンポーネントが作られます。

browserify でさくっと JavaScript をまとめます。

$ browserify -o bundle.js -t [ babelify --presets es2015 --presets react ] a.js

できた bundle.js を表示する HTML が以下です。

<!DOCTYPE html>
<meta charset="utf-8">
<div id="app"></div>
<script src="bundle.js"></script>

これを開くと App コンポーネントによって Hello, react-router! と表示されます。

ルーティング

これだけだと普通に表示するのと変わらないので幾つかページを作ってみます。

import React, { Component } from 'react';
import { render } from 'react-dom';
import { IndexRoute, Link, Router, Route, hashHistory } from 'react-router';

class App extends Component {
    render() {
        return (
            <div>
                <ul>
                    <li><Link to="/">index</Link></li>
                    <li><Link to="/a">page A</Link></li>
                    <li><Link to="/b">page B</Link></li>
                </ul>
                <div>
                    { this.props.children }
                </div>
            </div>
        );
    }
}

class Index extends Component {
    render() {
        return <div>Index</div>;
    }
}

class PageA extends Component {
    render() {
        return <div>A</div>;
    }
}

class PageB extends Component {
    render() {
        return <div>B</div>;
    }
}

render((
    <Router history={hashHistory}>
        <Route path="/" component={App}>
            <IndexRoute component={Index} />
            <Route path="/a" component={PageA} />
            <Route path="/b" component={PageB} />
        </Route>
    </Router>
), document.querySelector('#app'));

Link コンポーネントは URL へのリンクを作ってくれます。 hashHistory を使っている場合は自動で # をつけてくれるみたいです。

Route の子要素として IndexRouteRoute を作ると下のパスでマッチしたうえでコンポーネントを子要素として作ってくれます。例えばこのコードだと #/ を開いた時は <App><Index /></App> を、 #/a を開いた時は <App><PageA /></App> を、 #/b を開いた時は <App><PageB /></App> を表示します。

表示すると最初は Index と表示されている部分が、リンクをクリックすると A B と内容が変化するはずです。

パラメータ

/users/123 のように URL のなかにパラメータがあるパターンも試してみます。

import React, { Component } from 'react';
import { render } from 'react-dom';
import { IndexRoute, Link, Router, Route, hashHistory } from 'react-router';

class App extends Component {
    render() {
        return (
            <div>
                <ul>
                    <li><Link to="/">index</Link></li>
                    <li><Link to="/a">page A</Link></li>
                    <li><Link to="/b">page B</Link></li>
                </ul>
                <div>
                    { this.props.children }
                </div>
            </div>
        );
    }
}

class Index extends Component {
    render() {
        return <div>Index</div>;
    }
}

class Page extends Component {
    render() {
        return <div>{ this.props.params.id }</div>;
    }
}

render((
    <Router history={hashHistory}>
        <Route path="/" component={App}>
            <IndexRoute component={Index} />
            <Route path="/:id" component={Page} />
        </Route>
    </Router>
), document.querySelector('#app'));

path="/:id" の用に指定すると this.props.params.id にマッチした結果が入ります。 #/a を開くと a が、 #/b を開くと b が表示されます。