ReactとRedux触ってみた
TL;DR
- ReactとRedux触りながら勉強してますよー
- Reactのライフサイクル周りよくわかってない
- redux-form触ってて一見便利そうだったけどクソ重事案に引っかかって泣きそう
- そんな感じの日記(別に知見の共有とかはない)
最近雑用みたいな仕事が増えてきててそろそろフロントエンドも出来るようになっとかないとなーと思ってたところ、そういうフロントのツールを作って欲しいって頼まれたので前に保守頼まれてたツールを派生させる形でReactのwebを1から作ることにした話。
まずReactとReduxについて(納期も短いので)簡単に学ぶ。体系的には勉強してないのでコード書きながらこんな感じに理解した。多分色々違う。
React
- SPA/PWAに対応したjavascriptのフロントのフレームワーク
- 名前の通り特定の変数をvalueとして詰めることでリアクティブに値のDOMへの反映がまとめて出来る
- それぞれのコンポーネントのライフサイクルの処理をハンドリングして適宜処理を埋め込んでいく
- propsとしてデータを子に渡せるほかコンポーネントごとにstateとして変数を管理出来る(これをもとにリアクティブに値を反映する)
- よくinputのonChangeとvalueのところに対応するstateの値とそれを変更する関数を埋め込み忘れて入力が効かねえ!ってことになったりする
Redux
- Reactとよく組み合わせて使われるツールで上記のstateを管理する
- state(やりとりされるデータ)、store(state置き場)、action(stateを変更する処理)、dispatch(actionをstoreに保存するための手続き)、reducer(actionに対してstoreにこういう処理を行うってのをまとめたもの)ってのが用いられる
- stateは原則1つのみでjsonみたいな形で保持し、複数のreducerはユニークな名前空間を指定して利用するような形
- Reactとつなぎこむことでそれ単体だと分かりづらいstateの管理を一元管理してコンポーネントをまたいで値の反映をしやすくしている
今回使ったreact関係のツールは主に以下のもの。あと管理webだったのでadminlteをガワに採用した。
- react-router-dom
- immutable
- react-intl
- redux-thunk
- react-form
immutableはstateとしてrecordをextendsしたmodel的なクラスを登録しており、set(hoge).action()とした結果をdispatchするのに使っている(action関数はclass内定義したactionを返す関数)。
使ってみてハマったこと
redux-thunk
dispatchするときはaction渡さないといけないんだけど関数渡してもそれ実行するようにさせるよってmiddleware。違うかも。
最初は使わなくてもいけるんじゃねーの?と思ったが最初はモックで適当なもの同期で返してたものをAPI呼び出しに変えたところ"Actions must be plain objects"などと言われるようになってしまい採用することに。
modelのget系関数が返す結果はthisでconnect内のdispatchするタイミングでaction呼び出す(例:hoge: dispatch(state.model.a.getHoge().action()))つもりだったのだが、APIを介した非同期だとこのgetHogeはPromiseを返すことになってしまって色々難儀した。
redux-form
form管理するためのツール。storeの一部を借りてそこにformのデータを保持してsubmitが走った時に実際にstateへの適用およびそれに伴う処理を走らせる。
これに関しては面倒な部分が複数あった。
フィールドと値の関連系の処理がめんどい
初期値を渡す部分は別に普通にreduxから渡してる値を参照させることが出来るのだが、テキストでアドレスを渡す必要がある。例えばpropsでid,nameをフィールドに持つtableという値を渡していた場合、"table[1].name"とかを名前としてフィールドに渡すことで初期値の適用やフォームの構造が定義される。
いちいちこういう文字列を生成しないといけないが、ルールとして制約できるのは個人的には悪くないかなって気がした。どうせ自前でやっても似たようなことやることになるし。
あと非同期で取ったデータをdispatchしてそれをconnectした後initialValuesに埋め込む実装を行ったが、ライフサイクルの都合か取得後もう一度値の初期化を走らせないといけなかった。reduxFormでつなぎこむ際にenableReinitialize:trueの要素を入れておけば勝手にやってくれる。が、遅くはなる。
なんか重い
これが絶賛悩んでるところで、上記のような理由で値の初期化の回数が増えるのも影響としてあるのだが、フィールドが生成されるごとに"REGISTER_FIELD"のイベントが走ってDOMの更新が行われるだけではなく、親コンポーネントのcomponentDidMountも実行されてえらいことになってる。
具体的には総フィールド数が20で上のコンポーネントのcomponentDidMountでログイン確認のためのAPIを呼んでいる場合、20(フィールド数)x2(再初期化の分)=40回上のcomponentDidMountが走ってAPI呼び出しも行われるという始末になってる。
最後に書いたredux-formの遅さがこのアプリ開発の上での最後の問題で、これにぶち当たったのがGW入る1時間前とかだった。formikとかいうのがredux-formの代替として挙げられてるのを見たので触ってみようかと思う。
なぜ他の人たちがこのそうすることを勧めているのか、についてはその後ゆっくり腰を据えて考えてみようと思う。目下は場当たり的に物事に当たるスタンスでとりあえず乗り切りたい。