Cycle.js と Model-View-Intent
Cycle.js 。いろいろと悩み混乱しているのでここまでの理解をメモする。
Cycle.js では MVI (Model-View-Intent) という方針が示されている。これは公式のドキュメントに書かれている。
Intent の現状の理解は次のとおりだ。
Intent は drivers responses から actions をつくる。これは DOM Event や HTTP レスポンスなど drivers からの生の response からユーザーの意図している action に変換する。
たとえば、back button を click するのはユーザーの意図としては back という action をしたい、ということ。コードで表現すると次のようになる。
function intent(responses) { const { DOM } = responses; const actions = { back: DOM.select('button.back').events('click') }; return actions; }
responses は { DOM, HTTP }
のような形式で、これは Cycle.run(main, drivers)
の drivers に driver を登録した際のキーに対応している。
Model の現状の理解は次のとおりだ。
Model は状態を管理する。Intent から actions を受け取り state$ を返す。短い……。
例はこんな感じ。
import { Rx } from '@cycle/core'; function weight(actions) { return actions .changeWeight .startWith(70) .map(weight => ({ weight })); } function model(actions) { const state$ = Rx.Observable.combineLatest( weight(actions), (...args) => Object.assign(...args) ); return state$; }
正直なところ、かなり迷っている。
(...args) => Object.assign(...args)
でまとめるために .map(weight => ({ weight }))
のように各 props 側にまとめる処理をいれているところとか。
Observable.combineLatest で状態を { ... }
という state に整形してから流すようにしているけど HTTP driver のように必要なときにしか値を流すべきでないもの (null
や {}
を受け取ると死ぬ) に値を流さないようにするのが view の役割でいいのか……とか。
View の現状の理解は次のとおりだ。
View は state$ から driver requests をつくる。driver requests ってのが driver によってまちまちだけど DOM driver なら vtree$ だし HTTP driver なら request$ 。
これを一本の state$ から切り分けるの大丈夫なのかって感じではある。
import { h } from '@cycle/dom'; function DOM(state$) { return state$.map(({ weight }) => { return h('div', [ 'Weight: ' + weight + 'kg' ]); }); } function HTTP(state$) { return state$ .filter(({ request }) => request) // for null .map(({ request }) => request); } function view(state$) { return { DOM: DOM(state$), HTTP: HTTP(state$) } }
HTTP driver を使う際の Model-View の切り分けや状態が謎。
Model や Intent では {...args}
のために { prop }
や { action }
で返させていたのにここでは { driver }
じゃないのも謎。
いろいろ模索している、ということで。