次のReact状態管理はMobXにする理由

2017/12/29更新： [ReduxとMobXの選定観点](https://qiita.com/Takepepe/items/7cfa7cb99543ed372663) も併せて見ていただければ幸いです

[front-end-handbook-2017](https://www.gitbook.com/book/frontendmasters/front-end-handbook-2017/details) に名前が挙がっていた [MobX](https://mobx.js.org/) に興味が湧き調べてみました。その結果、掲題のとおりの記事を書きたくなるまでに至ったので、個人的にReduxより優れていると感じた点を挙げたいと思います。

- 記述量が圧倒的に減る
- Store概念のわかり易さ
- ~~バケツリレーが実質不要になる~~
- injectを活用するとjsxが純粋になる
- デコレーター層の存在

# 記述量が圧倒的に減る
一つの双方向な値をコンポーネントに表示するために、Reduxでは以下の作業が必要でした。

- Reducer に initialState として値を追加
- Reducer で Object.assign した新しい State を生成
- ActionType を追加
- ActionCreator を追加
- ActionDispatch する コンポーネントで ActionCreator をバインディング
- 表示を受けたいコンポーネントで、Reducer から props展開
- 必要があれば、表示を受けたいコンポーネントの親が props をバケツリレー
 
いくら慣れてもこの非効率感は否めません。
一方MobXでは、以下の様になります。

- Store に値を追加
- Store の値を操作する関数を追加
- Store を操作したいコンポーネントで 関数を実行
- Store を参照したいコンポーネントで observer を宣言

単純にdiffが削減されることが分かります。
とてもシンプルです。


# Store概念のわかり易さ
ReduxにおけるStoreは唯一無二で、Developerには隠蔽された状態で提供されます。そして Reducer を経由することで、Storeの値を参照できます。また、ActionType・ActionCreator を経由して Storeの値を変更できます。初学者が「Reduxとっつきにくい」と感じるのはここにあると思っています。

Mobxではそうではありません。
コンポーネントが参照したい Store を宣言し、参照します。
Store の値を変更したければ、Storeに依頼します。
とてもシンプルです。

```stores/SomeStore.js
export default class SomeStore {
  @observable count // 追加した値
  increment = () => this.count++ // 追加した関数
}
```

```components/SomeComponent.js
const SomeComponent = ({ someStore }) => {
  return (
    <div>
      <p>{ someStore.count }</p>
      <button onClick={ someStore.increment }> +1 </button>
    </div>
  )
})
// 関心のある Store を inject（注入） し、 observe（購読）する
export default inject('someStore')(observer(SomeComponent))
```


# ~~バケツリレーが実質不要になる~~
~~Reduxでは、コンポーネントがStoreの値を参照するため、Containerのpropsを、必要なコンポーネントまでバケツリレーで渡さなければいけません。中間コンポーネントは必要の無い props を受け流すだけの記述が必要になります。~~

~~Mobxではこの無駄な作業がありません。
コンポーネントが参照したい Store を宣言し、必要な値を、必要な形式で注入することが出来ます。バケツリレーが不要になったことで、コンポーネントをより純粋に保つことができます。~~
~~以下で詳しく説明していきます。~~

**【お詫びと訂正】**
本節は下記実装を採用した際の特記事項です。mobx-react v4.1.2 の injectは context参照を binding しています。React v15.4.2 においては、context参照は実験的で、将来的に利用できなくなる可能性があります。採用する際は、それらの動向を気にかけるようにしましょう。


# injectを活用するとjsxが純粋になる
Reduxでは、Container から渡された props の必要な参照を render前に展開し、jsxに注入します。MobXでも同様の手順が必要ですが、mobx-react の Provider と inject を使うことで見通しの良いコンポーネントを定義することができます。mobx-react v4 から追加された injectの `mapperFunction` がそれです。

- propsを抽象化できる
- propTypesの見通しが良くなる
- mappingを変更すれば、異なる observerComponent が export 出来る
- よりテスタブルになる

inject の mapperFunction により抽象化されたことで、再利用性が高くなったコンポーネントの例を挙げてみます。

```components/todos.js
import Mobx, { inject } from 'mobx-react'

export const Todos = ({ todos, count, add, remove }) => {
  return (
    <div className="todos">
      { todos.map((todo, i) => <Todo key={ i } index={ i } />) }
      <p className="todos-count">{ count }</p>
      <button onClick={ add }>addTodo</button>
      <button onClick={ remove }>removeTodo</button>
    </div>
  )
}

export const OpenedTodos inject(s => ({
  todos:  s.todoStore.openedTodos,
  count:  s.todoStore.openedCount,
  add:    s.todoStore.addTodo,
  remove: s.todoStore.removeTodo,
}))(Todos)

export const ClosedTodos inject(s => ({
  todos:  s.todoStore.closedTodos,
  count:  s.todoStore.closedCount,
  add:    s.todoStore.addTodo,
  remove: s.todoStore.removeTodo,
}))(Todos)

Todos.propTypes = {
  todos:  Mobx.PropTypes.observableArray,
  count:  React.PropTypes.number,
  add:    React.PropTypes.func,
  remove: React.PropTypes.func
}
```

2017/02/27[修正]:mapperFunctionを利用する場合は observer decorate を取り除く必要があるので修正しました

```
N.B. note that in this specific case neither NameDisplayer or UserNameDisplayer doesn't need to be decorated with observer, since the observable dereferencing is done in the mapper function
```

2017/03/09[修正]:injectは上記の通りcontext参照をbindingしているため、安定的な選択肢とは言えないため、内容を修正しました

# デコレーター層の存在
ここでいうデコレーター層とは `@` の es6 decorator ではありません。
Ruby on rails の DraperDecorator の様なもので、Store の `@computed` を指しています。
アプリケーションを構築するうえで、否応でもjsx中に表示に関するロジックが増えてきます。
そこを吸収してくれるのが `@computed` です。

`@observable` な値が変更されたとき、`@computed` な値も変更される挙動から、
自分のなかで、DraperDecoratorの様な利用が出来るなと感じました。

Reduxでも等価の処理をすることは可能ですが、
Reducerの見通しがどんどん悪くなってしまうことは想像し易いです。

# 総括
Reduxを導入する利点に DevTool や ドキュメントが充実している点があります。
MobXはまだまだこれから、という感じです。
自分の様に、redux-saga を使っていた場合は、非同期の扱いや、他の副作用は？という点も
考慮しないといけません。この点については、別の機会に書きたいと思っています。

2017/02/28[修正]:続編を公開しました >> [次のReact状態管理 MobXのStore考察](http://qiita.com/Takepepe/items/59d1396c25c8a699c41c)