Edited at

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


はじめに:Reactのstateはめんどくさい

stateには良いことが何一つない。

私はそう思っています。

stateは、


  • コンポーネントの作りが複雑であることを示している

  • テストがだるい

  • コンポーネント外からの参照がしづらい

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

唯一かつ最大のメリットと思っているのは、

機能をコンポーネントで完結させられることだと思います。

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


Statelessなコンポーネントに変えよう

まずは簡単なStatefulなコンポーネント、

MyTextを用意しました。

See the Pen 9683c6b277428eccb652_1 by tsuuuuu_san (@tsuuuuu_san) on CodePen.

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

コードを読めばティンときますが、

必須チェックと数値チェックが機能に入っています。

Statelessにしてみます。

See the Pen 9683c6b277428eccb652_2 by tsuuuuu_san (@tsuuuuu_san) on CodePen.

ロジックを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化を推し進めると出てくる問題 が解消されるでしょう。


例)コンポーネント用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コンポーネントも使いやすくなるよ!

ということです。

是非お試しあれ。