Mobx + Statelessコンポーネントには、ストレスがない

# はじめに：Reactのstateはめんどくさい
stateには良いことが何一つない。

私はそう思っています。

stateは、

- コンポーネントの作りが複雑であることを示している
- テストがだるい
- コンポーネント外からの参照がしづらい

と軽く挙げるだけでもデメリットがホイホイ出て来ます。

唯一かつ最大のメリットと思っているのは、
機能をコンポーネントで完結させられることだと思います。

これは配布や再利用にとても有効で、とにかく使いやすいと感じます。

# Statelessなコンポーネントに変えよう
まずは簡単なStatefulなコンポーネント、
`MyText`を用意しました。

<p data-height="265" data-theme-id="0" data-slug-hash="KrQJNV" data-default-tab="js,result" data-user="tsuuuuu_san" data-pen-title="9683c6b277428eccb652_1" class="codepen">See the Pen <a href="https://codepen.io/tsuuuuu_san/pen/KrQJNV/">9683c6b277428eccb652_1</a> by tsuuuuu_san (<a href="https://codepen.io/tsuuuuu_san">@tsuuuuu_san</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

適当に入力してみてください。

コードを読めばティンときますが、
必須チェックと数値チェックが機能に入っています。

Statelessにしてみます。

<p data-height="265" data-theme-id="0" data-slug-hash="PxQLNN" data-default-tab="js,result" data-user="tsuuuuu_san" data-pen-title="9683c6b277428eccb652_2" class="codepen">See the Pen <a href="https://codepen.io/tsuuuuu_san/pen/PxQLNN/">9683c6b277428eccb652_2</a> by tsuuuuu_san (<a href="https://codepen.io/tsuuuuu_san">@tsuuuuu_san</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

ロジックをMyStore classに移しただけじゃねーか！
ですって？

ちょっとそれは後にしてください。

先にこれを質問させてください。

**MyTextのコードを見て！・・・使いやすそうですか？**

多分大半の人は、使いづらそうと思っているのではないでしょうか。

# 本気だからこそ、Stateless Component
お気付きの方もいると思いますが
上記の一見使いづらそうなコンポーネントは、
**自由度が飛躍的に向上** しています！

サンプルのerrorsに注目して見ましょう。

errorsはValidationの結果に応じて、メッセージが格納されます。

Statefulな場合、次の特徴があります。

- 入力すると勝手に必須と数値チェックが走る
- チェックの内容はerrorsに格納される
- 初回Renderで必須エラーが出てしまう
- つまり、これ単体で使いやすい

Statelessの場合、次の特徴があります。

- 適切なpropsを設定しないと入力チェックは走らない
- つまりこれ単体をポンと置いても望んだ動作は得られない
- ただし、好きなValidationが入れられる
- さらに好きなタイミングでValidationをかけられるように調整できる
- つまり、自由度が高い
- 自由度が高いので、急な仕様変更にも対応しやすい
- HOCしてしまえば、Statefulな特徴を持ったコンポーネントも作れる

現場にもよりけりでしょうが、
Stateless化にメリットを感じる方も少なくはないのではないでしょうか。

# Stateless化を推し進めると出てくる問題
結局のところ、データやロジックをどこかに寄せる問題が出てきます。
さらにStoreを取り入れていない場合、propsの伝搬を考えるのに一苦労です。

# Mobxをご存知でしょうか

一言でいいますと、Storeです。

Reduxの複雑な手順を全て取っ払って、Storeだけにしたような感じです。

Mobxはその自由度がゆえに、ぶっちゃけ**使いづらい**です。

PJや要件などに合わせ、適切にStoreを設計しなければ、
破綻すること間違いなしです！

しかし・・

コンポーネントを操作しやすいように、
それに対応したStoreを用意するくらいなら、別に設計なんて必要ありません。

既存のStore設計の邪魔もしないでしょう。

# MobxでコンポーネントにPropsStoreを与える
MobxStoreはどこからでも参照できます。

さらに、使い方は単純です。

- Storeを作る
- Storeをどこからでも呼べるようにRootコンポーネントにProviderを仕込む
- 使いたいStoreをinjectして、Storeを参照する

だけです。

**Stateless化を推し進めると出てくる問題** が解消されるでしょう。

```js:例)コンポーネント用Store
import {observable, computed, action} from 'mobx';

export default class MyTextStore {
  /* MyTextのprops.valueにセットする */
  @observable
  value = '';

  /* MyTextのprops.onChangeにセットする */
  @action
  changeValue = value => {
    this.value = value;
  }

  /* MyTextのprops.errorsにセットする */
  @computed
  get errors() {
    const { value } = this;
    const errors = [];
    if (value === '') errors.push('必須だよ');
    if (isNaN(value)) errors.push('数字でお願い');
    this.setState({ value, errors });
    return errors;
  }
}

/*
※ Mobxよくわからない人向けの説明
@observableが付いていると、値に変更があった時にreRenderが走ります。
@actionが付いているものは、@observableな値を明示的に更新する関数です。
@computedは、@observableの変更に伴い、自動で計算されるものです。
*/
```

こんなStoreを作成して、コンポーネントにセットします。
(
　面白いのは、セット方法もこれまた自由なのがMobxクォリティ。
　すでにMobxStoreを用いているなら、そのStoreから参照できるようにしても良いですし、
　そのままinjectして、classプロパティをセットしてもいいでしょう。
　props用のObjectを作成する関数をStoreに用意しても良いですし、
　HOCを使うのもありかもしれません。
)

Stateless化 + MobxStoreのコンボは、Statelessコンポーネントのメリットを加速させます！

- MobxStoreを用いることで、Stateを持っているかのような動作が可能になる
- MobxStoreはどこからでも参照できます
  - Submitボタン押下で再Validation!なんかもできちゃう

# 外伝：MobxStoreは、どこまでも進化する！？
上記で`MyText`に特化したMobxStoreの例を紹介しましたが、
これはあくまで一例にすぎません。

例えば、オブジェクト指向を併用すれば、
値やValidationはオブジェクトにロジックを寄せられるかもしれません。

コンポーネント単位でStoreを用意することが大変なら、
AtomicDesignの大きい単位で用意するのもありかもしれません。

なんてめんどくさい！Mobx！

しかし、より良いStoreの使い方を見つけた時のあなたの脳汁はきっとダバダバです。

# 最後に
Mobxを使おう！的な記事になったような気もしますが、そんなつもりはありません・・。

とにかくいいたかったのは、

**極力Statelessコンポーネントを作ろう！
Mobx使うとStatelessコンポーネントも使いやすくなるよ！**

ということです。

是非お試しあれ。