MobX 3 released: Unpeeling the onion – Michel Weststrate – Medium
作者の @mweststrate 氏による記事も出てましたねー。
`2.7.0`まで理想的な使い方をしてきたなら、特に大きな変更があるわけではないバージョンアップです。
ただにわかに人気が出つつある気もするので、長いことMobX使ってるマンとして書いておきまーす。
変更点まとめ
詳しくは↑のログを見てもらうとして、個人的に大きなのは、
- APIの刷新
- `observable`な値の作り方が変わった
- Modifierが消えた
- Bound actions
- エラーハンドリングの強化
- その他
って感じですね。
APIの刷新
MobX的Observableな値を作るためのコードスタイルが少し変わります。
タイプごとの`observable.*`
`2.7.0`までのMobXは、どんな型の値でも共通のAPIでObservable化してました。
// 2.7.0 const arr = observable([]); const obj = observable({});
これが、
// 3.0.0 const arr = observable.array([]); const obj = observable.object({}); const any = observable({});
という用に、APIとしても型を明示できるようになりました。
っても中の挙動は変わってないし、`observable()`をそのまま使えば値によって今まで通りに使えます。
さよならModifier
`2.7.0`までのMobXは、渡されたオブジェクトを勝手に「再帰的に」Observableにしてました。
APIからの返り値をただ入れておきたいみたいな場合、巨大なJSONを再帰されても・・ってな場合には、Modifierを使ってました。
// 2.7.0 // 再帰 observable(response) // 1階層だけ observable(asFlat(response))
ただこのModifier、APIのデザインとして適当・・?っていう話が前からあり。
左辺で定義したいとか、flatってなんやねんとか、そもそもいらねーんじゃねーのとか。
今回のAPI刷新に伴い、ここも変更されました。
// 3.0.0 // 再帰 observable.object(response) // 1階層だけ observable.shallowObject(response)
Observableな値を作るには、`observable.*()`なメソッドを使うということで、よりわかりやすくなりました。
この`observable.*`を何層か組合せて実現していくのを、オニオンアーキテクチャというらしい。冒頭の記事のタイトルにもなってる考え方ですね。
// 2.7.0 class Store { constructor() { extendObservable(this, { foo: asFlat([]), bar: asMap({}), }); } } // 3.0.0 class Store { constructor() { extendObservable(this, { foo: observable.shallow([]), bar: observable.map({}), }); } }
`extendObservable`の場合もこのような感じ。
生き残りModifier
↑のコードを見て、`shallow`にはしたいけど型不定な場合どうしたらいいの?とか、Decorators派なんですけど・・って人もいるので、
- `observable.deep`
- `observable.ref`
- `observable.shallow`
この3つは引き続きModifierとして使えます。
// 2.7.0 class Store { constructor() { extendObservable(this, { todos: asFlat([]) }) } } class DecoStore { @observable todos = asFlat([]) } // 3.0.0 class Store { constructor() { extendObservable(this, { todos: observable.shallow([]) }) } } class DecoStore { @observable.shallow todos = [] }
ちなみにModifierは、`@observable`、`extendObservable`、`observable.object`でしか使いません。
オブジェクトもクローンされるように
// 3.0.0 const { observable } = mobx; const a = { x: 1 }; const o = observable(a); o.x = 2; console.log(a.x === o.x) // false
`2.7.0`までは、これが`true`でした。
まあそんなに驚きはない・・かと。
ArrayとかMapは今までクローンされてたので、こっちのほうが自然ですね。
Bound actions
個人的に欲しかったやーつ。
公式のログからまるっと持ってきましたが、
class Ticker { @observable tick = 0 @action.bound increment() { this.tick++ // 'this' will always be correct } } const ticker = new Ticker() setInterval(ticker.increment, 1000)
というわけで、`@action`の明示と`bind(this)`がまとめてできるように!
いやー`action`化するのと`bind`するので`prototype`を2周するのアレだったんですよねー。
ちなみに、`action`化するというのは・・、
`MobX.useStrict(true)`することで、`action`化されたメソッド以外でObservableな値を変更すると警告が出る(そのおかげで関心の分離ができる)ようになります。
そのため、このメソッドは`action`ですと明示することが必要で、それです。
エラーハンドリングの強化
`2.7.0`までのMobXは、なんかエラーや例外があったときに、catchしてwarningをログに出してくれてました。
ただこうしてしまうと、MobXに問題があるのか、ユーザーが書いたコードに問題があってのものなのか、見分けがつきにくかったです。そしてだいたいMobXに問題は無い。
`3.0.0`からのMobXは、なんの警告も出さなくなって、エラーハンドリングは全てユーザーに委ねられるようになります。
その他
他にもBreakingな変更もあるんですけど、そこまで影響ないと思うので割愛。
- DepricatedなAPIがいろいろ消えたり
- Flowで型の恩恵が得られるようになったり
もしてます。
Reduxはいらんと思ってるけど生Reactは辛い・・そういうあなたにオススメなMobXの2017年にこうご期待。
というわけで個人的には割と理想の使い方をしてきてたので、Modifierを一括置換するだけで移行完了でした。