reactjs
redux
mobx

ReduxとMobXの選定観点

この記事を書いた当時、MobXは余計な複雑さが隠蔽され、とても良く目に映りました。何よりReduxにはOOPが可能なモデルが無い、という当時のReduxに対する不満を解決してくれました。(今はReduxにもモデルを持ち込む手法を選んでいるので不満はありませんが)

直近、同僚に「Redux と MobX の選択はどうすれば?」と相談されたので、その日の返答内容をまとめてみました。

TODOサンプルだけでは測れない現実の課題

ライブラリのキャッチアップに、TODOや「+1」するカウンターで検証することはよくあると思います。ただし、これだけでは長期運用するプロダクトの検証には不十分です。 MobXは便利で簡単に始めることが出来るため、Storeを無作為に参照しあってエンバグしやすいコードが出来てしまいがちです。

Storeが数えるほどしかなく、機能も単純であれば原因を突き止めるのは容易です。しかしながら、現実は技術要件とビジネス要件が入り組みます。Store 同士のロジックが密結合してしまい、そこに依存するアプリケーションを組んでしまうと、技術負債の出来上がりです。

Storeが密結合してきた場合

Storeが密結合してきたら、Storeの結合点を外に逃すのが得策かと思います。複数の reaction を用意して、Storeとは切り離されたレイヤーで Observable な値に対してマッピングを行います。こうすることで、各々の Store は疎結合になり、ユニットテストも容易になります。

entry_point.js
const storeA = new StoreA()
const storeB = new StoreB()
const storeC = new StoreC()

reactions({ storeA, storeB, storeC })

reactions.js
export default function ({ storeA, storeB, storeC }) {
  reaction(
    () => storeA.value,
    value => { storeC.value = value * storeB.value }
  )
  reaction(
    () => storeB.value,
    value => { storeC.value = value * storeA.value }
  )
}

難点は、Store が増えるほどトランザクションが追い辛くなることです。リファクタ・スケールにおいては、簡単な部類ではないと思います。普段から出来ることといえば、他の Store が別の Store の状態を直接参照しないようにすることぐらいです。

MobXにする境界

1.異なるプラットフォームでドメインモデルを共有したい

React + MobX と紹介されることが多々ありますが、両者は疎結合で MobX をドメインモデルとすることが可能です。別の view で再利用可能で、データバインディングフレームワークと比べ、ドメインモデルが切り離されています。

そのため react-dom・ReactNative でドメインモデルを共有することもなども容易です。もしこの点に魅力がなければ、Reactに固執する必要もないかもしれません。

2.Storeの密結合が起こりえない

json を取ってきて SPA で記事を表示するだけ、というユースケースの場合は、Store の結合は問題にならないかと思います。非SPAの場合も同様で、MobX が最適解だと思います。

3.とにかく早く ReactComponent を書き上げたい

Redux もそうですが、React とは疎結合なため、状態管理は変更可能です。ReactComponent はそのままで、状態管理だけ差し替えることが出来ます。「Store結合が複雑な大型改修が必要になったら、Reduxに乗り換える」ぐらいの心意気があれば、最初は MobX の方がシンプルに目指しているものが作れるかと思います。

【こんな案件だったら自分はMobXを選ぶ】

  • エンドポイントによっては部分的にSPA
  • メディア媒体・シンプルな投稿機能サイト
  • 期間限定のプロモーションサイト
  • 個人のブログ

Reduxにする境界

1.異なるプラットフォームでドメインモデルを共有したい

こちらもまた、Reactとは疎結合なのでドメインを切り離すことが出来ます。普通の使い方をすると、Reduxはデータソースの保持のみでドメインモデル不在ですが、immutable.js を導入すればOOPが可能なドメインモデルを持ち込めます。Reducer の switch 文 やボイラープレートが辛いと感じる方は、こちらを参考にしてみてください。私自身の直近業務では、よく見る3種のボイラープレートは一切 git commit していません。(それ相当のものはもちろんcommitしています)

2.モデル同士の複雑な密結合が予想される

redux-saga は複雑だと敬遠されがちですが、非同期処理だけのものではありません。上記の様な MobXStore 密結合問題と同様に、redux-saga上でモデル間のマッピングが行えます。

一番の特徴は、そのトランザクションの組み替えが容易なこと。async/await と使い勝手が同じなので、学習コストが無駄になることが無いです。

ActionType名称を変更しても、Objectでマッピングされているため、悩み事は最小限で済みます。(redux-saga 以外に redux-observable でも可能です)

3.書き捨てではなく、長期的に保守していきたい

先に述べたとおり、リファクタに強いです。後々の保守工数を鑑みても、長期運用やスケーラビリティはこちらに分があります。

【こんな案件だったら自分はReduxを選ぶ】

  • 全エンドポイント SPA
  • 機能豊富な WebBrowser アプリ
  • 機能各種が密結合する ReactNative アプリ
  • 複数人でのフロントエンドコード長期保守

最後に

  • 作るもの・締め切りなどによって最適解は変わります。
  • ボイラープレートが辛い、という消去法で MobX にするのはやめましょう。
  • 少し工夫でボイラープレートは気にならなくなります。
  • Store や Model が純粋であることは尊いです。テストが書きづらく感じたら、赤信号です。
  • 両者のpros/cons を理解して最適解を選びましょう。
  • もしかしたら、React以外の選択もあるかもしれません。