英語も簡単だし分かりやすくて良い。おすすめ。以下メモです。
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とかやればいいかなあ。