ハッカーを目指す白Tのブログ

夏は白のTシャツを着ています。ラジオが好きです。(ラジオネーム: 隠れキリジダン)

React初心者のためのreact-routerの使い方

2015年1月、React ConferenceにてReact.jsでネイティブアプリが作れるようになると発表された。この発表を受けて最近何かと話題のReact.jsだが、リリースされたのは2013年であり、2009年にリリースされたAngularJSや、2010年のBackbonejsなどの他のjavascriptフレームワークに比べると新しく、まだネットに日本語の情報があまりない。ということで、React.jsのデファクトルーティングライブラリであるreact-routerの使い方についてまとめてみた。

1. そもそもreact-routerとは

react-routerは、React.jsのデファクトのルーティングライブラリである。Ember.jsのrouting APIを基に作られていて、ヘッダー、サイドバー、フッターなどが複雑にネストされたビューのルーティングの設定を容易にしてくれる。なぜ容易になるのかというと、ビューのコンポーネント(ヘッダー、サイドバー、フッター等)のネスト構造と同じように、ルーティングの設定もネスト構造で定義することで、両者の関係性が単純化されるからである。

2. react-routerのインストール方法

それでは、さっそくreact-routerを触ってみよう。インストール方法はいたって簡単で、パソコンにnode.jsさえインストールされていれば、

npm install react-router

でインストールできる。

また、以下説明する例を試すにはjsファイルの先頭において以下をrequireしなければならない。

var Router = require('react-router');
var Route = Router.Route;
var NotFoundRoute = Router.NotFoundRoute;
var DefaultRoute = Router.DefaultRoute;
var Link = Router.Link;
var RouteHandler = Router.RouteHandler;

これは、CommonJSという、JavaScriptで様々なアプリケーションを作るための標準仕様に則っている。

3. react-routerの使用例

では、実際にどのように使われているのか。Dashboard・Inbox・Calenderの3つのページからなる、小さなWEBアプリケーションを例に説明する。以下の図が、そのアプリケーションのUIの設計図であり、全てのページで共通のヘッダーが使われるとする。

f:id:beck23:20150218183748p:plain
このアプリケーションを、react-routerを使わずに開発する場合、そのルーティングを定義するコードは以下のようになる。

var Header = React.createClass({
  render: function () {
    return (
      <header>
        <ul>
          <li><a href="/">Dashboard</a></li>
          <li><a href="/inbox">Inbox</a></li>
          <li><a href="/calendar">Calendar</a></li>
        </ul>
        Logged in as Jane
      </header>
    );
  }
});

var DashboardRoute = React.createClass({
  render: function () {
    return (
      <div>
        <Header/>
        <Dashboard/>
      </div>
    );
  }
});

var InboxRoute = React.createClass({
  render: function () {
    return (
      <div>
        <Header/>
        <Inbox/>
      </div>
    );
  }
});

var CalendarRoute = React.createClass({
  render: function () {
    return (
      <div>
        <Header/>
        <Calendar/>
      </div>
    );
  }
});

otherRouter.route('/', function () {
  React.render(<DashboardRoute/>, document.body);
});

otherRouter.route('/inbox', function () {
  React.render(<InboxRoute/>, document.body);
});

otherRouter.route('/calendar', function () {
  React.render(<CalendarRoute/>, document.body);
});

見て分かるように、この場合各コンポーネントで共通のヘッダーを呼びださなければならず、コードの重複が増えてしまう。

これに対しreact-routerを使うと、ルーティングを定義するコードは以下のようになる。

var Router = require('react-router'); 
var DefaultRoute = Router.DefaultRoute;
var Link = Router.Link;
var Route = Router.Route;
var RouteHandler = Router.RouteHandler;

var App = React.createClass({
  render: function () {
    return (
      <div>
        <header>
          <ul>
            <li><Link to="app">Dashboard</Link></li>
            <li><Link to="inbox">Inbox</Link></li>
            <li><Link to="calendar">Calendar</Link></li>
          </ul>
          Logged in as Jane
        </header>

//<RouteHandler/>コンポーネントをrenderすることで、
//ルーティングで定義された全てのアクティブな子コンポーネントを呼び出す。        
        <RouteHandler/>
      </div>
    );
  }
});

//アプリケーションのurlの階層構造をネストされた<Route/>タグで定義し、
//各<Route/>のhandlerにReactのコンポーネントを紐付け、
//render時には、アクティブな<Route/>のhandlerがコンポーネントを呼び出し、
//それらが融合されて表出される。
var routes = (
  <Route name="app" path="/" handler={App}>
    <Route name="inbox" handler={Inbox}/>
    <Route name="calendar" handler={Calendar}/>
    <DefaultRoute handler={Dashboard}/>
  </Route>
);

//React RouterはリクエストされたURLに最も深くマッチする<Route/>を探し出し、
//その木構造の中での枝に含まれる全ての<Route/>をアクティブにする。
Router.run(routes, function (Handler) {
  React.render(<Handler/>, document.body);
});

例として挙げたアプリケーションが単純なUIなため、コード量としてはさほど変わってはいないが、react-routerを使用することで、一箇所でルーティングを管理することが可能となり、ネストされたUIを作るときの生産性、保守性が上がる。また、ルーティングを設定しないとビューが表出されないことから、URLの構造を先に考えつつ開発を行う事ができる。これらのメリットは、UIが複雑になればなるほど役立つものとなる。

4. react-routerのコンポーネント一覧


1. RouteHandler

<RouteHandler/>は、その子要素のアクティブな <Route />のハンドラーを呼び出す。

2. Link

<Link >はtoオプションで指定したnameの <Route >のハンドラーを呼び出す。

// このようなルーティングの設定があった場合
<Route name="user" path="/users/:userId"/>

// 以下のリンクコンポーネントで、上記の<Route/>のハンドラーを呼び出せる。
<Link to="user" params={{userId: "123"}}/>

paramsオプションに対しては、渡したいRouteのpathのダイナミックセグメントと一致するname,valueのオブジェクトを渡す。

//ダイナミックセグメントとは、pathの一部で:から始まるId等を指定している箇所のこと
<Route name="user" path="/users/:userId"/>
//このRouteの場合、:userIdの部分を示す。

3. Route

<Route handler={App}>
  <Route name="about" handler={About}/>
//pathが指定されていない場合、pathはnameから自動的に補完される。
//この場合<Route name="about" path="about" handler={About}/>となる。
  <Route name="users" handler={Users}>
    <Route name="user" handler={User} path="/user/:id"/>
  </Route>
</Route>

<Route >はそのネスト構造によりアプリケーションのルーティングを定義しとhandlerでviewの階層構造を定義する。

<Route />のオプション
name: routeの名前。Linkコンポーネントで使われる。
path: ドメイン以下のurl。未定義の場合、nameから定義され、nameも未定義の場合、デフォルトで'/'になる。
handler: routeがアクティブなときに呼び出されるコンポーネント

4. DefaultRoute

<DefaultRoute>は親のrouteのpathと完全に一致する時アクティブになる。

<Routes>
  <Route path="/" handler={App}>

    <!--
  pathが'/'の時、以下の<DefaultRoute handler={Home}/>がアクティブになる。
    -->
    <DefaultRoute handler={Home}/>

    <Route name="about" handler={About}/>
    <Route name="users" handler={Users}>
      <Route name="user" handler={User} path="/user/:id"/>

      <!--pathが'/user'のとき、以下の <DefaultRoute handler={Home}/>がアクティブになる。-->
      <DefaultRoute name="users-index" handler={UsersIndex}/>

    </Route>
  </Route>
</Routes>

5. NotFoundRoute

<lNotFoundRoute>は、リクエストされたurlが親の<Route/>のpathとマッチするのに、そのいずれの子<Route/>ともマッチしない場合にアクティブになる。エラーページの作成等に使える。

<Route path="/" handler={App}>
  <Route name="course" path="course/:courseId" handler={Course}>
    <Route name="course-dashboard" path="dashboard" handler={Dashboard}/>

    <!--  `/course/123/foo` というurlがリクエストされた場合、
    以下の  <NotFoundRoute />がアクティブになる。-->
    <NotFoundRoute handler={CourseRouteNotFound} />
  </Route>

  <!--  `/flkjasdf`というurlがリクエストされた場合、
    以下の  <NotFoundRoute />がアクティブになる。 -->
  <NotFoundRoute handler={NotFound} />
</Route>

6. Redirect

<Redirect>は、fromオプションで設定したpathへのリクエストがあったときに、toオプションで指定したpathにリダイレクトするようにする。

<!--
  開発中にurlの名称等を変更したくなった場合に使える。
   `/get-in-touch`というpathにリクエストを送ると 、
 `/contact`というpathをrenderするようにする。
-->
<Route handler={App}>
  <Route name="contact" handler={Contact}/>
  <Route name="about-user" path="about/:userId" handler={UserProfile}/>
  <Route name="course" path="course/:courseId">
    <Route name="course-dashboard" path="dashboard" handler={Dashboard}/>
    <Route name="course-assignments" path="assignments" handler={Assignments}/>
  </Route>

  <!-- `/get-in-touch` -> `/contact` -->
  <Redirect from="get-in-touch" to="contact" />
</Route>

7. State

アクティブなparams,query,routeの情報を必要とするコンポーネントのためのmixin。ダイナミックセグメントを含む<Route>のhandlerが呼び出すコンポーネント等で使われる。

// route
<Route name="user" path="user/:name" handler={User} />

// handler
var User = React.createClass({
//mixinは、mixinsに配列で渡す。
  mixins: [ Router.State ],

  render: function () {
//アクティブなparamsの情報を取得している。
    var name = this.getParams().name;
    return (
      <div>
        <h1>{name}</h1>
      </div>
    );
  }
});

<State />のインスタンスメソッド
getPath: アクティブなURLを返す。
getParams: アクティブなparamsを返す。
getQuery: アクティブなqueryを返す。

5. Routerのrunメソッド

最後にRouterのrunメソッドについて紹介する。runメソッドは、リクエストされたURLとマッチするrouteを特定し、それらのhandlerに紐づくコンポーネントをラップし、コールバックにhandlerとstateを引き渡す。

Router.run(routes, [location,] callback(handler, state))

//第二引数のHistoryLocationによって、HTML5のhistory APIを使えるようになる
//第三引数のコールバック関数である無名関数の第一引数はHandlerで、
//マッチした<Route/>の全てのコンポーネントがラップされている
Router.run(routes, HistoryLocation, function (Handler) {
  // whenever the url changes, this callback is called again
  React.render(<Handler/>, document.body);
});

参照URL

rackt/react-router · GitHub

以上