書こうと思った気持ち
react+reduxを使る機会があったので、その辺で感じた事を書き連ねます。この記事では主にreact使ってみての感想なので、reduxと絡めた話はまた別記事で。
react使おうかなー、どうしよっかなー、という人の参考になれば幸いです。
reactの深い技術についての話は他のみなさんが書いてくれていると思うので、ここが良かったとか、ここではまった、みたいな実体験話がメインです。
ベストプラクティスとは思っていませんが、とりあえずそういうやり方もあるんだなぁ、くらいな気持ちで読んでもらえれば。
使おうと思った理由
BackboneとjQueryでメンテされているUIを変えるチャンスが来たのが発端。現状だとviewにロジック書いてたりmodelにview寄りの処理が書いてあったりとだいぶ宇宙の法則が乱れていて辛かったので、UI変えるなら下回りの技術も変えよう、と思った。
みなさんご存知の通りjsのフレームワーク、Viewライブラリはangular、ember.js、vue.jsとかほんといろいろあって、1、2年したら廃れているみたいなことがざらにある感じなので、全フレームワークを一通り触ってみるとかそんなことはしてません。仮想DOM便利そうという自分の感覚と、昨年のadvent calendarとか見る限り使われ来ている雰囲気を感じたので、何かトラブってもなんとかなるだろう、と踏ん切りをつけてreactに決めた。
(実際にはここからfluxとかどうよ?みたいな話があるがそれは別)
以下、個別のトピックスごとに雑感を。
仮想DOMについて
UIのあるべき状態を考えるだけで良くなるのはすごい良かった。ものすごくシンプルな例だと、クリックしたらリストを開く(ここではclassがactiveだったら開いたUIになるとする)、みたいなのは下のように書ける。jQueryであればDOMを取得してaddClassする、というのをclickHandler内でやることになると思うが、reactではclickHandler内ではコンポーネントの状態を変えるだけで、render内でその状態に応じてあるべきhtml(ここではulのクラス名が異なる)を定義するだけで良い。差分更新についてはreactに任せておけ。動的なUIなんだけど、静的なUIを作っている感じで書いていけるのがすごい良かった。
clickHandler() {
this.setState({opened: true});
}
render() {
return (
<div>
<ul className={this.state.opened ? 'active' : 'inactive'>
<li>...</li>
<li>...</li>
</ul>
<span onClick={this.clickHandler.bind(this)}>開く</span>
</div>
);
}
state, props
ほとんどstateは使わなかった。というのも、アプリとして持つべき状態は全てreduxで管理していたので、reduxで管理しているアプリ状態をpropsに渡すだけ、というのがほとんどだった。ただし、コンポーネント単体で完結するようなもの、例えばセレクトボックスを開いている、閉じている、といったものはコンポーネントのstateで管理した。reduxで管理すべきだったのかもしれないが、それくらいはcomponent内で完結させた方がわかりやすかろう、という判断でそうした。
コンポーネントの切り方
小さい要素は再利用可能な形で切っていく
細かく刻んだ方が良いと思う。丸っと一つのでかいcomponentにすると一部の状態が変っただけでコンポーネント全体の仮想DOMの再描画処理が走ってしまうのはもったいない。動的に値が変りうるとか、あるいはhtmlの標準的な挙動以外をjsでさせたい要素に関しては、それ用のコンポーネントとして切った方が良いかなと感じた。
たとえば画像表示一つにしたって、遅延ロードさせたりロード後に画像の縦横比を見て向きを変えたり、not foundの場合はダミー画像を出したりなど細かいロジックを持つので、ImageLoader
みたいな小さいコンポーネントに切り出して、再利用できる形にしていくと保守性がだいぶ高まる。
似ているようで微妙に異なるUI
一方で、ほとんど似ているけど詳細で微妙に異なる、というUIはコンポーネントの切り方が難しかった。ほとんど一緒なので共通化してフラグをpropsで渡してうまくやろう、みたいな欲を出すと、仕様の変遷につれてフラグの嵐になって地獄になる。かといって共通化部分を親コンポーネントクラスにして継承で解決しようとすると、親のUI仕様を一部変えたら子コンポーネントで思わぬ副作用が、となりがち。
アクセスしているユーザに応じて一部だけ表示ロジックを変えたい、というのはよくあるパターンだがここでユーザの状態に密接にしたフラグを渡すと地獄になる。たとえば管理者向けのときだけ編集用コンポーネントを出したいから
<ItemList isAdmin/>
とかやり始めて、一般ログインユーザでもやらせたいからと
<ItemList isLoginUser/>
となり、じゃあ非ログインのときはフラグ無し
<ItemList/>
かな、とかなって辛い感じになってくる。それよりは、
<UserListView editable/>
といった感じで、見ているユーザ状態を表すflagよりも、そのコンポーネント単体で見たときにどういう状態か、というフラグで制御できるようにした方が、コンポーネントの独立性が高くなって良さそう(な気がする)。この辺は自分でやってみてうまくいかなかったところなのでみんなどうしてんの?という感じ。
個人的には、アプリとしての状態をうまくUIとしての状態に変換してやらないと、コンポーネントの独立性が崩れて地獄になりそうだな、という感じです。
keyについて
まじ重要。つけないとまずDOMの差分更新時に性能が出ない、とかいう話もあるが、componentのmount, unmountに関係してくるので、componentDidMount
とかで何かするようなことを書いているとそこではまるかもしれない。
一覧表示のためのItemList
があって、カテゴリのタブを切り替える度にItemListのpropsで表示内容を渡すような実装をしていたら要注意。
http://qiita.com/koba04/items/a4d23245d246c53cd49d にも重要性は書かれているけど、レンダーの度に新しいItemListオブジェクトを作っているような気分でいても、react的にはオブジェクトが使い回されているためstateの初期化とかイベントハンドリングとかで因果関係がわかりにくい副作用を生むことになる。表示したいものが変る場合(itemsが変ると言うより、タグとかカテゴリとかページが変る場合)はそれに応じたユニークなkeyをセットしたほうが幸せになれると思う。
reactが出すkey周りのwarningは無視せず早急につぶす事をオススメします。
jQueryとのつきあい方
一部使ってます。componentDidUpdateとかであればrender処理が終わっているので、DOMを取得して高さを取得するとかそんなことをやってます。が、そのためだけにjQueryを読み込むのはもったいないので、極力使わないで済むなら使わずにやる方が良いです。
react component
jQuery uiほど充実している感じはないですが、けっこう出てきています。が、まだまだ発展途上の感もあるので、みなさんぜひ業務で作るコンポーネントは再利用可能な感じにして公開していきましょう!
私はこの辺で探した。
http://react.rocks/
https://github.com/rackt
childrenの使いどころ
Dialog系とかnot found時の表示に使った。DialogコンポーネントではOKやキャンセルなどの枠だけ定義しておいて、中に表示したいメッセージなどはchildrenで渡す、みたいな感じ。
render () {
return (
<div className='dialog'>
{this.props.children}
<ul>
<li><a className='ok-button'>OK</a></li>
<li><a className='cancel-button'>キャンセル</a></li>
</ul>
</div>
);
}
枠だけは決まっていて、中身だけ差し替えたい時は積極的に使いたいパターン。
全体的な評価
key周りでハマったり、jQuery uiのような便利コンポーネントがなくて苦労したりもしたけど、動的に変るUIを静的UIっぽい感じで作り込んでいけるのが非常に良かったです。結局、stateなり親コンポーネントからもらうpropsなり、ある状態に従うUIを書く、というのがシンプルで良いです。backboneとjQueryでごりごりやってた頃に比べればはるかに楽になりました。「追加ボタンを押したらなんか二つ増えたけど」とか「クリックしても色変らないんだけど」みたいなDOM操作に起因するようなバグはあまり出なかったなぁという感じです。それだけ見ても、選んで良かったReact、という感じです。