SPA を作る上で実装したいのがルーティング。React を使ったアプリでのルーティング実装には react-router を使う。 今回からインストールは npm で。
npm install --save react-router
react-router を使って Master-Detail の簡単なサンプルを書いてみた。 react-router が提供する Router や Route は React コンポーネントなので、 DOM を組み立てる感覚でルーティングを定義できて面白い。
var React = require("react"); var ReactDOM = require("react-dom"); var ReactRouter = require("react-router"); var Router = ReactRouter.Router; var Link = ReactRouter.Link; var Route = ReactRouter.Route; var IndexRoute = ReactRouter.IndexRoute; // ユーザー情報を格納したストア var initialUsers = [ { id: 1, name: "香川", age: 26 }, { id: 2, name: "本田", age: 29 }, { id: 3, name: "長友", age: 29 }, { id: 4, name: "岡崎", age: 29 }, { id: 5, name: "内田", age: 28 }, { id: 6, name: "清武", age: 25 } ]; var User = React.createClass({ render: function() { return ( <tr> <td> <Link to={"/users/" + this.props.user.id} query={{name: this.props.user.name}}> {this.props.user.name} </Link> </td> </tr> ); } }); var Users = React.createClass({ getInitialState: function() { return { users: initialUsers }; }, getUsers: function() { return this.state.users.map(function(u) { return <User user={u} />; }); }, render: function() { return ( <div className="users"> <h1>ユーザー一覧</h1> <table> <thead> <tr> <th>名前</th> </tr> </thead> <tbody> {this.getUsers()} </tbody> </table> </div> ); } }); var UserDetail = React.createClass({ render: function() { // パスに埋め込まれたパラメータは params から取得できる var userId = this.props.params.id; // ユーザーを取得 var user = initialUsers[userId - 1]; // 名前はクエリパラメータで受け取ったものを表示してみる return ( <div className="user-detail"> <h1>ユーザー詳細</h1> <dl> <dt>ID</dt> <dd>{user.id}</dd> <dt>名前</dt> <dd>{this.props.location.query.name}</dd> <dt>年齢</dt> <dd>{user.age}</dd> </dl> </div> ); } }); var About = React.createClass({ render: function() { return ( <div className="about"> <h1>このサンプルについて</h1> <p>react-router のサンプルです。</p> </div> ); } }); var MyApp = React.createClass({ render: function() { return ( <div> <ul> <li><Link to="/users">ユーザー一覧</Link></li> <li><Link to="/about">このサンプルについて</Link></li> </ul> <div> {this.props.children} </div> </div> ); } }); // Router や Route も React コンポーネント ReactDOM.render(( <Router> <Route path="/" component={MyApp}> <IndexRoute component={Users}/> <Route path="users" component={Users}/> <Route path="users/:id" component={UserDetail}/> <Route path="about" component={About}/> </Route> </Router> ), document.getElementById("content") );
ルーティングにマッチしたコンポーネントは props.children、 パスに埋め込まれたパラメータは props.params に格納される。
Link でクエリパラメータを渡せるけど、id やフラグといった、ちょっとした情報を渡す程度にとどめて置くのが無難。 すべての情報を渡すのは適してない。 そのため、id をもとにストアから詳細な情報を取り出す必要があった。
今回も「コンテナコンポーネントに状態をもたせて子コンポーネントはステートレスにする」設計をなんとか実装したかったが、 上手いやり方が思いつかず断念。react-router に精通すれば可能なんだろうか。 今後の課題だな。