はじめに: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化を推し進めると出てくる問題 が解消されるでしょう。
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コンポーネントも使いやすくなるよ!
ということです。
是非お試しあれ。