Angularの状態管理にMobXはいかがでしょうか?
Angular Advent Calendar 2018 19日目の記事です!
Angularの状態管理といえば、NgRxが一般的ですが、最近色々選択肢もいろいろでてきました。NgRxのReduxのような状態管理を行わずに、AngularのシングルトンなServiceを利用してRxJSのみ行うこともできますし、Akita のようなシンプルな状態管理のライブラリもでてます。(Angularのシンプルな状態管理ライブラリ Akita について - Qiita で紹介されてます!)
そこで今回はMobX というよりシンプルで簡単な状態管理のライブラリを紹介したいと思います。
MobXとは
Simple, scalable state management
公式サイトでこう謳ってるとおり、とてもシンプルだけど拡張性の高い状態管理のライブラリです。 MobX公式のフロー図はこのような形です。
ref: https://mobx.js.org
- Action
- stateを更新する関数。stateはActionからのみ更新できる(オプション)
- State
- 状態管理するObject。Stateの集合を
Store
という単位で管理するのが一般的
- 状態管理するObject。Stateの集合を
- Computed
- stateを加工するgetter。Stateが更新さえると自動的に計算される
- Reactions
- Stateの変更を検知して、別のActionを実行したり、処理を実行したりできる
サンプルコードでわかるAngular + MobX
基本的なところをサンプルコードを交えて説明したいと思います。 angular-mobxはサンプルコードを公開してくれているので、これで説明したいと思います。
@ observable
import { observable } from 'mobx'; @observable todos = [];
状態管理したいObjectに @observable
というDecoratorを付与することで、監視対象にすることができます。
この値が変更されると、後述する @computed
の値が更新されたり、 autorun
が実行されたりします。
また、mobx-angularの *mobxAutorun
ディレクティブなどと合わせて使用することにより、更新後自動的にChange Detectionが走ります
@Action
import { action } from 'mobx'; @action addTodo({ title, completed = false }) { this.todos.push(new Todo({ title, completed })); } // actionには名前をつけられる // https://github.com/mobxjs/mobx-devtools などで発火するActionがみやすくなる @action('remove todo') removeTodo(todo) { const index = this.todos.indexOf(todo); this.todos.splice(index, 1); }
@action
はStateを操作するためのメソッドに付与します。
MobXのデフォルトの設定ではStateはどこからでも更新が可能です。が、より厳格なState管理をするためにもStateはActionからのみの更新にしたいものです。そのためには以下の設定をいれることで可能になります。
import { enforceActions } from 'mobx'; configure({ enforceActions: 'always' });
なお、上記の設定にすると@actionのメソッド内でも非同期処理場合はstateを直接更新できません。その場合は runInAction()
をあわせて使います。
@action('add todo') async addTodo(todo: any) { const result = await this.http.post('/api/todo', todo) runInAction('add todo success', () => { this.todo.push(result) }) }
@computed
import { computed } from 'mobx'; @computed get filteredTodos() { return this.filter !== 'SHOW_ALL' ? this._filter(this.todos, this.filter) : this.todos; }
Stateを計算した新しい値を返却するgetterに付与します。公式サイトの説明ではスプレットシートの関数のようなものといっています。 あるstateが変更されれば自動的に計算され、値を取得することができます。
autorun
import { autorun } from 'mobx'; autorun(() => { localStorage.todos = JSON.stringify(toJS(this.todos)); localStorage.filter = JSON.stringify(toJS(this.filter)); });
autorun
はコールバック内のstateが変更されたら自動的に実行されます。これにより、stateの副作用を自動的に解決できるので、state操作のみを中注することができます。autorun
は定義時にも実行され、その時実行されたstateを監視対象にする動きのようです。
似たようなものに reaction、whenがあります
MobXの良いところ悪いところ
良いところ
- 必要なファイル数が少ない
- Reduxみたいに一つの操作をするために何個もファイルを編集する必要がない
- 習得のハードルが低い
- フロントエンド専任でないエンジニアでもすぐ仕組みを理解してコードを書き始められた
- 副作用を勝手に解決してくれるので気にするべき値の操作を限定できる
computed
、autorun
など
- コントリビューターが多い。スター数も多い(※ MobX)
- 死なないライブラリだと使うの安心できる
悪いところ
- 自由な反面、決まったルールがほとんどないのでちゃんとした設計が必要
- Storeの分割やComponentへの接続方法
- 副作用の解決方法が点在しているどこでどう更新されるのかあとからわからなくなる
- Angular + MobXの情報がほとんどない
最後に
egghead.ioでReactでの例ではありますが簡単なMobXのチュートリアルが公開されてます。興味がある方はぜひ見てみてください。