はじめてReact.jsでアプリ書いてはまったこと[ES6含む]

適当なデータをCRUDするための管理画面みたいなものを
Reactを使って実装してみました。

前の記事にも書きましたが、僕のJavascriptレベルは
jQueryによる保守運用、AngularJSを、先輩の書いたコードを参考に少しやったことある程度です。

今回はじめてReact.js(ES6)でfluxの実装色々やってみて
僕がはまったところをまとめてみました。

[ES6] class構文で書いてReact.Componentを継承してコンポーネントを定義する場合mixinは使えない

多重継承がないので、まぁ仕方ないですが、表題通り。
React.createClassではmixinsという値にミックスインしたいオブジェクトを
入れるだけでしたが、これが使えません。

ただ、react-routeみたいなのを自分で
実装するのはかなりしんどいです。

そこで非公式ではありますがreact-mixin
というプラグインが公開されています。

使い方は

こんな感じ。

ただ、これでも使えないプラグインがあって、ちょっとちゃんとは調べられていないんですが
これみたい
getDefaultPropsでpropsとろうとすると基本的に使えません。

react-sortableに関しては、比較的短く読みやすいコードだったので
自分が使いやすいようにforkして変えてどうにか使えるようにしました。
(nestedのほうやexampleは使ってないのでsortableだけ、ですが)

react-sortable(fork版 ed6対応)

Componentのライフサイクルはよーく覚える

なんとなく覚えてると、コンポーネント増えてきたときに混乱してくる。
設計がよければ、そうでもないのかな?

基本的には以下

二回目以降、の部分で呼び出されるライフサイクルメソッドは他にもあるんですが
とりあえず重要なものを。

詳しくは公式を参照してください。

ReactのチュートリアルだけじゃなくてFluxのチュートリアルも写経してみたほうがいい(Flux使わなくても)

ページ数も少なくてデータ構造も単純なら、Reactだけでいいんですが、
比較的複雑な構造をやり取りする必要がある場合は、Fluxでの実装を検討してください。

以下僕の反省

Reactのチュートリアルではコンポーネント同士で
データをやり取りを行っていますがすぐに限界(書いてる人がつらくなる)が来ます。

それでFluxに沿った実装をやってみるんですが、Storeの位置づけにピンと来るまでが難しかった。
(サーバサイドでいうデータベース的位置づけなの?
サーバサイドのデータとはどう整合性取るのがいいの?
サーバサイドのAPI叩くのはStoreがやるの?
みたいなことが漠然していた。)

結局コード読んだり、リファレンス読んでるだけじゃわからなくて
Fluxのチュートリアル
を全部写経してみました。

そこで『こうすれば早く理解できたかな』というのが

  • 一旦サーバサイドのことは忘れる(クライアントサイドだけで完結するアプリをイメージ)
    • 僕の場合はどうしてもサーバサイドと切り離して考えるのに時間がかかってしまいました。
    • 結局サーバからJsonとか取得するのはどこでやればいいの?みたいのばかり悩んでた
    • 一旦それは置いといていい。
  • Storeとはデータ置き場(そのまんまやがなwwwと言わずにw)
    • この中で複雑なことはやらない、あくまで自分が管理してるデータをどうするか、だけ
  • あとはActionCreatorが投げてくるActionをListenするだけ。
    • ここはまんまチュートリアル通りで問題ないと思います。

の2点で、特に後者。
最初はStoreで全部やることになるんじゃないの?って思ってました。

[ES6] class構文で書く時は各メソッドのbind(this)を忘れずに

結構めんどくさいし、忘れて意味不明な挙動することがあるんですが、忘れないように。

こんな感じで(独自Componentで拡張した)テキストボックス内が変更されたら_onChange呼び出して、そこで自分の
state内のfooを取り出そうとしています。が、this.state.fooはundefinedになります。

これは_onChangeがHogeクラスにbindされておらず、FugaInputにされているからです。
FugaInputにstate.fooがなければundefinedになります。

これをちゃんとコンストラクタでbindしてやる必要があります。

これでOK。
ES6でReactのコンポーネントを書く際の注意が以下にまとまっています。

React on ES6+
React #ES6 class

ものすごいザックリ言うと

  • propTypes,defaultPropsはstaticで宣言してね
  • getInitialStateはconstructorで設定してね
  • 独自メソッドはbind(this)するかES6のArrow function使ってね
  • mixinは使えません、mixinに代わる方法は検討しています><
  • Arrow function使えばstateナシ(Stateless)の簡素なコンポーネントも作れますよ

みたいな感じです。

Componentに渡す値は参照渡しされています!

これは当たり前っちゃ、当たり前なんですが、僕はハマりました・・・
お恥ずかしい・・・

例えばJavaで

こんな感じでfunc内でfの値を書き換えるとmainメソッドのf1まで
書き換わってしまいます。これは参照渡しでf1を渡しているから、というのは
まぁ当然なんですが、以下はReactで本質的に同じことをしています。

この状態でテキストボックスの値を変更すると、Hogeのstate.aまで変更されます。
それはHogeのstate.aの参照をFugaのprops.aとして渡し、それをそのままFugaのstate.aに
設定しているからです。

JSXで書いてるからか、setStateとかしてるからか、僕は勝手にコピーされてるものと
勘違いして、↑のような例を最初やってしまいました・・・。
(Storeに渡してない、親のコールバック読んでるわけでもないのに、子のstate更新したら親の値まで変わって混乱した)

propsはimmutableでstateは自分自身の状態を持つようにするべきですが、このおかげで
一気にめちゃくちゃに、Fluxの一方通行はぐちゃぐちゃになります。

これを避けるためには

  1. propsには値のみを渡す
  2. propsに渡すオブジェクトは全く別の参照を持ったコピーを渡す
  3. 利用側でコピーする
  4. propsに渡す値はImmutableにする(immutable.jsの利用)

の3つが考えられますが、最初は不便すぎてさすがに論外。
2か3か面倒かつコピーを忘れるリスクもあるけど、簡単。
しかし、明らかにダサいし、忘れずコピーする、っていうのもつらすぎる。

4が最善だと思ってますが、後から導入すると結構しんどい
(経験談。言語でImmutableをサポートしているわけではないので、API利用しないと値が取れない)

とりあえず、で作ったオブジェクトをコピーするものが以下。
可能なら最初からImmutable.jsを利用する方向で進めたほうがいいとは思います。

https://gist.github.com/KentaKomai/abc7d31ecfe893839942

Immutable.jsを利用する場合は注意が必要で
Immutable.MapImmutable.Listはネストしたオブジェクト構造には対応していません。
そのため

みたいなデータ構造だと、aとbしかimmutableになりません。

となってしまいます。このようなデータ構造はImmutable.fromJSImmutable.toJSを利用しましょう。

Immutable.js

加えて、子Componentに渡すpropsは、最低限にしたほうがよいですね。
無駄な値をまとめて渡したくなるから、結局複雑なオブジェクトを渡すハメになる。
(でもpropsの種類いっぱい渡す、のもつらいんだよなぁ・・・・)

さいごに

多分ほかにも結構はまったけど、思い出したのはこんな感じです。
バリデーションとかサーバサイドのやり取りとかについても、今度書きたいな。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です