良さ気なFlux実装、Marty.js個人的まとめ


2015/12/03 追記: MartyのReadmeより
Martyはもうメンテをしないので、reduxかaltを使う事が推奨されるようです。ありがとうMarty。

以下過去の記事内容


Flux Comparisonで色々眺めていたり、twitterを追っかけていたり自分で触ってみたりして、
Marty.jsが個人的に本当に良い気がしている。

Martyの特徴

作者と質問者の会話より

ユーザーとのこんなやりとりがあり、Martyの特徴が要約されているのかなと思ったのでテキトーな意訳として載せておく。

https://news.ycombinator.com/item?id=8923245

質問:fluxxorと何が違うのか?
回答:
他のFlux実装は、Fluxっぽいデータフェッチ(APIなどのことだと思う)なヘルパーが無い。コンポーネントとのストアのバインディングのボイラープレートの方向に向かっている。あとデバッグ機能。他のはあんまりデバッグ機能拡充してない
Martyは下記のことを助ける

  • コールバック無しの非同期データフェッチ
  • viewのためのState Mixin
  • Chrome Extensionのデバッガー

ということでこの辺りが特徴らしい

  • コールバック無しの非同期データフェッチ
  • viewのためのState Mixin
  • Chrome Extensionのデバッガー

Facebook Fluxとの対比

公式の図とMartyにおけるAPIの対応がどうなっているかをまとめる

だいぶ雑な図だけど僕の中でこんなイメージ。

対応表

Flux Marty
Web API Utils State Source
Action Creators Action Creators
Store Store
Change Events
Store Queries
State Mixin
Dispatcher Constants(*)
Callbacks Constants(*)

(*) DispatcherとConstantsなどは、隠蔽している関係などで、直接1:1ではなくなっている、このへんは自分なりの理解。

有益なサンプル

そもそもまだそんなに見つけられなかったのだけど、見るならこのへん

各機能のざっくり説明など

だいたいコードとかは公式サンプル持ってきたりしています

State Source

WebAPIUtilに相当する、データ取得の実装を巻き取る層。
Martyの目玉機能の一つだし僕が「これいいなあ」となった部分の一つ。

UserAPI.js
var UserAPI = Marty.createStateSource({
  type: 'http',
  baseUrl: 'http://foo.com',
  getUsers: function () {
    // github/fetch 形式での取得(後述)
    return this.get('/users').then(function (res) {
      // 完了したら、ActionCreatorを呼ぶ
      SourceActionCreators.receiveUsers(res.body);
    });
  },
  createUser: function (user) {
    return this.post('/users', { body: user });
  }
});

こんな感じでthis.getなどで通信処理ができる。
HTTPの通信処理はisomophic-fetchが使われているようなので何も考えずにpromiseな感じで書いていける。

またHTTPだけでなくLocal StorageやSession Storageへのアクセスヘルパーも用意してくれている模様(未調査)

Constants

このあたりからは他のflux実装とわりと似てくる。

Constants.js
var Constants = Marty.createConstants([
  'RECEIVE_USERS',
  'DELETE_USER'
]);
  • ほかのflux実装にもよくあるconstantとほぼ一緒。
  • Actionとして発火される定数を定義
  • オブジェクトとしてconstantを階層化することもできる
  • 各Constantsが、Actionを発火することのできるAction Creatorの関数となっている(という認識)
  • {Action Type}_STARTING{Action Type}_DONE{Action Type}_FAILEDなど、特定の接尾辞のついた定義をすることによってPromiseがらみの処理ができるらしい(未調査)

Action Creators

これもわりと他にもよくある実装。

  • ちょっとこれは理解するの時間がかかった。
  • 言ってしまえばConstantsから生まれるAction Creatorの集合体。
  • action.rollback()などで失敗時にロールバックもできるらしい。(未調査)
  • 体感としてこのあたりどうしても若干冗長に感じる
ActionCreatro.js
var UserConstants = Marty.createConstants(["UPDATE_EMAIL"]);

var UserActionCreators = Marty.createActionCreators({
  updateEmail: UserConstants.UPDATE_EMAIL(function (userId, email) {
    this.dispatch(userId, email) //例
  })
});

UserActionCreators.updateEmail(123, "[email protected]");
  • また注意点として、この部分の処理は()=>なarrow functionを利用するとthisのスコープが変更されてしまうので、避けるようにとのアナウンスがある。
    • 個人的には余り気にならないが、がっかりする人いそうだ
    • 関連pull req

Stores

  • だいたい他のfluxと同等のStore
  • handlersの定義でConstantのAction Creatorを指定している。
  • handlersの定義は、複数のConstantsを受けたりとか色々複雑な指定ができる模様(未調査)
Store.js
var UsersStore = Marty.createStore({
  displayName: 'Users',
  handlers: {
    addUser: Constants.RECEIVE_USER
  },
  getInitialState: function () {
    return {};
  },
  addUser: function (user) {
    this.state[user.id] = user;
    this.hasChanged();
  }
});

State Mixin

  • State <=> Component(ReactComponent)をつなぐ部分
  • 結構公式だと長いサンプルがあるが、単純にStoreとComponentをつなぐなら簡易に書ける
UserComponent.js
var UserState = Marty.createStateMixin(UserStore);

var UserComponent = React.createClass({
  mixins : [UserState],
    :
    :
})
  • 上記は結構sugger syntaxっぽい気もするが、複数のStoreをListenしたりとか細かめに設定することも可能(未調査)
UserState.js
var UserState = Marty.createStateMixin({
  listenTo: [UserStore, FooStore]
});

Debug機能

なんかまだうまく使えてないので未調査。

結論

Martyええぞ。