英語も簡単だし分かりやすくて良い。おすすめ。以下メモです。
React Router Tutorial
2: ルートを描画する
Router
がRoute
を管理するRoute
にはコンポーネントと対応するpathを渡すRouter
にはhashHistory
を渡してやる。history
は色々オブジェクト渡せそうなので履歴をいじったりも出来るのかな?
3: Linkでナビゲーション
react-router
に入っているLink
を使うとreact-router
で管理しているRoute
へのリンクを作れる。
4: ネストしたルート
5: アクティブなリンク
- あるリンクが選択されているときにスタイルを当てる。(例: 見ているルートのリンクは赤色にする)
activeStyle
属性にCSSのobjectを渡せばOK.activeClassName
属性にクラス名を渡すと、アクティブになったときにそのクラス名が付く。style.css
とかで設定可能になる。
- いちいち
Link
にactiveClassName
とか指定するの面倒だから、NavLink
というコンポーネントを作っちゃおう。
6: パラメータ
- SinatraやExpressと同じように、URLに含まれた文字列を取得することができる。
Route
のpath
属性には/repos/:userName/:repoName
という感じでいつもの用に指定する。Route
で指定したcomponent
のprops
にparams
というオブジェクトがあるのでそれを使えば良い:this.props.params
7: もっとネスト
- 特に新しい事柄はなし。
Link
で指定したactiveClass
などは、親のLink
にも適用される。- たとえば、
/users/10/stars
へのLink
があって、それがactiveになっている時、/users/10/stars
のLink
だけでなく、/users/10
のLink
にも適用されるというわけ。
- たとえば、
8: インデックスのルート
- 今の実装だと、ある
Route
の/
には何もコンテンツが表示されないことになってしまう。 this.props.children
が無いときに表示するコンテンツを用意しよう。- やり方は2つ。
||
で区切る
{this.props.children || <Home/>}
と書けばよい。これはシンプルでいいしうまく動くけど、以下の理由のために他のRoute
と同じようにインデックスのルートを指定したい。
route
やcomponent
に応じてデータをfetchしてくるときのためonEnter
をHookするため- コードの分離のため
と、色々需要があるので、Route
みたいに指定したいぞ…。
IndexRoute
を使う。
Route
の中で、他のRoute
と同じようにIndexRoute
を使う。- 注目すべきは
IndexRoute
の属性。path
はつけていない。 - そして、
this.props.children
として渡されている。
- 注目すべきは
9: インデックスへのリンク
- よくある「ロゴをクリックしたら
/
へ」の実装。 Link
で実装すると(我々はNavLink
を使っているので実際はそれだけど)、ずっとそのリンクがactive
になってしまう。(/
はどのルートにも入っているので)- そこで
IndexLink
を使う。IndexLink
はLink
をwrapしたもの。Link
コンポーネントのonlyActiveOnIndex
にtrue
を渡せば良い。
- というわけで、
NavLink
にもその機能は適用されるので、実は全部NavLink
で済む。- [thought] Reactは継承より構成を尊重すべきだったはずなので、色々な今ポー年tのができてしまうよりはこうしてカスタマイズでなんとかできたほうが良いと思う。
10: browserHistory
でURLをきれいに
- URLを見るとルーティングのために
#
が使われている。- 昔Twitterが使っていて話題になっていた。
- 参考: さらなる「#!」URL批判 - karasuyamatenguの日記
- 昔はHistoryを弄れなかったので、こういう背景があったけれど。最近のモダンなブラウザなら、HTTPリクエストを行わずにURLをいじれる。
hashHistory
の代わりにbrowserHistory
を使えばOK。
でも…。
/repos
にルートを切り替えて、リフレッシュすると404になる。webpack-dev-server
に--history-api-fallback
をする。- すると
/
以外のpath
でもindex.html
が配信される。- nginxとかの場合は
try_files
とか使えば実装できる。
- nginxとかの場合は
- JSやCSSなど、assetのパスには注意。
11: 本番に似た環境でトライ
- 実際はSSR(Server-side Rendering)したかったりするので、それの準備。
- webpackしたものをexpressで配信する構成。
- webpack側でoptimizeかけられる。
webpack.optimize.DedupePlugin()
: 被ったモジュールは一つに。webpack.optimize.OccurrenceOrderPlugin()
: IDの桁数を減らしてサイズ節約webpack.optimize.UglifyJsPlugin()
: uglifyでminimizeする- ref: webpackを使い倒す - Thujikun blog
- express側ではgzip圧縮するために
compression
を使う。
12: プログラマティックに移動
- 今までは
Link
で移動してきたけど、ボタンクリックやフォーム送信などのタイミングで移動するようにしてみる。 browserHistory.push(path)
を使えば良い。
There's a potential problem with this though. If you pass a different history to Router than you use here, it won't work. It's not very common to use anything other than browserHistory, so this is acceptable practice. If you're concerned about it, you can make a module that exports the history you want to use across the app, or...
の部分がいまいち理解できなかった…。
contextTypes
に追加すると、this.context.router
でアクセスできるようになる。push
とかpop
ってmethodが生えてる。
なぜ contextTypes
に追加すると this.context.router
で諸々にアクセスできるのか
- RTFM: Context - React
Context
は、子や孫、その先のコンポーネントに対してprops
でのバケツリレーを防ぐためのもの。- 親のコンポーネントの
getChildContext
で返されたオブジェクトが、その子達からはthis.context
でアクセスできる。 react-router
のRouter
あたりがcontext
に入れてくれている。
13: サーバーサイドでレンダリング
node.jsでJSX
- nodeはJSXを理解しない(し、するべきでない)ので、コンパイルしないといけない。でも
babel/register
などはproduction環境での利用にはフィットしない。 - server用のbundleを作ろう。nodeのスクリプトもwebpackしてから実行する。
コードを分ける
- サーバーとクライアントの両方からrequireし易いように、
routes
の中身だけ切り出す。Router
のroutes
に渡してやればClientはOK。
サーバーサイドで描画!
- サーバーサイドでは、ReactのroutesとURLのパスを
react-router
のmatch
に渡してやる。RouterContext
はRouter
が描画するときに使うもの。というかこれをそのまま返している。Router
は描画にbrowserHistory
を使うけど、サーバーはステートレスなのでmatch
でhistoryをprops
に詰めて渡している。
- 整理すると:
- 描画自体は
RouterContext
が行っている。 - そのためにbrowserHistoryの情報が必要。
- browserHistoryの生成を
match
が担当している。 - という理解で良いのかな。
- 描画自体は
ハマったところ
staticなfileのディレクトリを指定するところ、オプションに{index:false}
入れておかないとCSSやJSが配信されなくて毎回HTTPリクエストが走ってしまった。
app.use(express.static(__dirname + '/public'), {index: false})
おわり
前回に引き続きreact-router
のチュートリアルを済ませた。大体理解したので、また何か作ってみたいなあという雰囲気。あとはRedux
とかやればいいかなあ。