React+ReduxをES2015で書いた話


最初に

この記事はWHITEPLUS Advent Calendar 2016 21日目になります。

こんにちは。株式会社ホワイトプラス、エンジニアの @knakamigawa です。

私はホワイトプラスでは、
- 衣類オンラインクリーニングのリネット
- 布団オンラインクリーニングのふとんリネット
- 靴オンラインクリーニングのくつリネット

の各サービスをお客様が触れるWeb全般について
フロントエンドからサーバーサイドまで広く担当しています。

リネット(特に前置きしない限りは衣類をさします)では、
クリーニングの申し込みフォーム(以降フォーム)をReact + Reduxで実装しています。
リネットのフォームはなかなか複雑な仕様のために、以前使用していたjqueryでの実装に限界を感じ、
システム開発チームにて検討の結果ReactとRedux、ES2015にて実装することを採用しました。

弊社での事例に基づいた実装のご紹介をしたいと思います。
(簡単なReact + Reduxの解説も入れますが、ある程度React + Reduxを知っている前提です)

登場人物

React

近頃流行のJavascriptフロントエンド開発フレームワーク。
謳い文句は「A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES」
「ユーザーインターフェースを作るためのJavaScriptライブラリ」ですかね。

従来のjQueryのようにHTMLで作成されたDOMをゴリゴリいじるという考えでは無く、JavaScriptによっていちからDOMを生成するVirtualDOMという考えです。

いちからといっても1度生成したVirtualDOMは差分が出た場合は差分のみ更新する仕組みなのでパフォーマンスも良いです。

なによりも、HTMLによるDOM表現から解き放たれることにより圧倒的な自由度が生まれます。
HTMLとJavaScript、さらに言えばサーバーサイドのテンプレートなどソースコードの分断あった従来の構成からほぼjsで書けるため保守性がグンと上がります。

Redux

「Redux is a predictable state container for JavaScript apps.」
「ReduxはJavaScriptアプリのための予測可能なstateコンテナです。」直訳するとなんのこっちゃという感じですが。
誤解を恐れずにざっくり言ってしまうと状態管理の為のフレームワークです。

本当は図解すると良いのですが言葉で説明すると(詳しく正しい理解は他の解説している記事などおすすめします)

ReduxはViewからActionCreatorを実行し、Actionを発行。
発行されたActionをDispatchするとReducerにて元のstoreを元に新しいstoreを作成して返却します。

これまたざっくりした言い方をすると、Actionを発火させるとReducerがstoreのデータを更新して返してくれると言うことです。

ES2015

言わずと知れたJavaScriptの基本仕様をまとめたECMAScriptの新バージョン2015。
まだまだブラウザ対応が道半ばなので、実際に動かすにはBabelというトランスパイラを用いて旧来のES5へ変換して動かします。

個人的な注目点は
- let, constの導入(変数、定数がJavaのようなブロックでのスコープを持てる)
- アロー関数(functionではなく関数型っぽい無名関数が()=>{}な構文で書ける、arrayのmapやfilterで大活躍)
- セミコロンレス(行末の「;」を書かなくてよい)

開発環境

webpack + babel

webpackを用いてパッケージングとbabelのトランスパイルを行います。
(記述薄いですがこの部分は割とよくある話なのでググると詳しい記事出てきます)

webpack-dev-server

開発に際して、毎回手動ビルドするのは手間ですし時間も掛かるので開発時はwebpack-dev-serverを用いて開発を行います。
(こっちも記述薄いですがこの部分は割とよくある話なのでググると詳しい記事出てきます)

React + Reduxでこんな実装したよ

サマリ

  • PHPテンプレートで表示して、bundle.jsの置換をしている(SSRじゃないよ!)
  • ActionやReducerは太らせない、ビジネスロジック的な物はmiddlewareに書く
  • API呼び出しもmiddleware
  • 【反省点】compornentsにビジネスロジックのようなものがはいってしまった

もうちょっと詳しく

PHPテンプレートで表示して、bundle.jsの置換をしている(SSRじゃないよ!)

Reactといえば最近はNode.jsでレンダリングしてHTMLを返すSSR(Server Side Rendering)が流行りですが、
PHPテンプレートを用いて表示し、divを置換するやり方をしています。(React的にオーソドックスなやり方、のはず)

index.html
<div id="root"></div>
<script type="text/javascript" src="/static/js/bundle.js"></script>
index.js
const rootElement = document.getElementById('root');
render(
  (
    <div>Hello, React world!</div>
  ),
  rootElement
);

ActionやReducerは太らせない、ビジネスロジック的な物はmiddlewareに書く

太らせないというよりは単機能以外持たせるべきでははい。というスタンスで開発を行いました。
ある値を更新するActionが他の副作用を持たないように、ActionとReducerを記述します。
一つのアクションで複数の値を更新したい場合、middlewareにてActionを適宜発火しそれぞれのReducerでstoreを更新します。

API呼び出しもmiddleware

middlewareでAPIを呼び出すように実装し、redux-thunkを用いて非同期呼び出しを行っています。
APIからレスポンスが帰るとActionが発火してReduserからstoreに反映され画面が更新されます。

【反省点】compornentsにビジネスロジックのようなものがはいってしまった

フォームの仕様として、状態によって発火するアクションを変えたい場合など、
middlewareではなくcompornentsにて投げるActionを変えるような制御を一部入れてしまっています。

一つのActionでmiddlewareでstoreの値によって別のActionを投げ直すよう、
制御をするのがよかったのかなーと今になって思うこともあります。

polyfill

今ES2015を使って書く以上避けて通れない道。

polyfillとは新しい機能など、ブラウザが対応していない場合に同等機能を従来の書き方でオーバーライドするような事を言います。
ブラウザ対応が十分に進んだ後は不要になります。が、現状最新のGoogle Chrome以外は割と必要です。

以下が今回適用したpolyfillたち。

code polyfill(npm)
fetch() whatwg-fetch
Object.assign object-assign
Symbol es6-symbol
Array.find es6-shim

今回はwebpackにてpolyfillを適用しています。

結び

React + Reduxはフレームワークと最初に言ってしまいましたが、あまりカッチリと実装方法が定義されていないため。
始めにチーム内で「どのような設計方針にするのか」を決めて進めるのが重要だと感じました。

思った以上に実例を交えられていない、ふんわりと雰囲気だけな投稿になってしまいました。
来年はもっときちんとした技術解説を書けるようしっかり準備したいと思います。


明日は弊社デザイナー makkoruri の「デザイナーがベンチャーって働くってどうなのよ!」です。

ホワイトプラスではエンジニアを募集しています

ホワイトプラスでは、ES2015を業務でバリバリ書きたい!という技術で事業に貢献したいエンジニアを募集しております。