2015-10-30 Developers in KOBE にいった
Developers in KOBE
Developers in KOBE - connpass に参加した。
興味があったのは ClojureScript くらい。Clojure ユーザーは貴重なので。
もくもくと bouzuya/cycle-gh-tree を修正していた。詳細は次項。
Cycle.js での HTTP リクエスト
Cycle.js で HTTP リクエストを意図せず大量に投げてしまう問題があったので記録しておく。
これは下記のようなエントリポイントの場合に起きやすい。つまり view が requests ここで言うと { DOM, HTTP } を返す場合に起きやすい。
Cycle.run(
(responses => view(model(intent(responses)))),
{
DOM: makeDOMDriver('#app'),
HTTP: makeHTTPDriver()
}
);
上記のような構成の場合 view は requests、model は state$、intent は actions を返すと思う。これを前提として view の例を書くとこんな感じ。
export default function(state$) {
const vtree$ = state$.map(/* ... */);
const request$ = state$.flatMap(({ request$ }) => request$);
const requests = {
DOM: vtree$,
HTTP: request$
};
return requests;
}
この例では state$ で流れてくるものに request$ property を持つものを流している。state$.request$ にする手もあるけどこちらは美しくない。別に state$.flatMap(({ requests }) => Rx.Observable.from(requests)) としても良い。
ここまでが前置きだ。ぼくが聞きたいのはここから。
DOM は Virtual DOM (virtual-dom) に食わせるので何度同じものを送りつけても害はない(はず)。
では HTTP はどうか。
ぼくは @r7kamura/cycle-fetcher-driver を使っているがこれはイメージで言うと HTTP.map(request => fetch(request)) していて、流れてくる request に対して fetch を呼び出して返すだけのものだ。@cycle/http もおそらく似たような動きをするだろう。
何が言いたいかというと DOM は同じものを流しても再実行はされないが、 HTTP は同じものを流すと再実行される。おまけに流れるタイミングは DOM と HTTP とが連動している (model のつくりによる、ぼくはそうなっていた)。意識していないと state$ に入れた request$ は何度も流れてしまう。
二重でリクエストをしないような仕組み (Virtual DOM のように差分管理して差分のみリクエスト) を考えないといけないけど、ひとまずは最後に request$ に追加したいイベントが来た時だけ残すようにした。
具体例。
2015-10-14 に書いた state に action$ を modFn$ に変換して scan で適用する方式をとっていて。
fetchIssues$ action で state.request$ に追加している。
このままだと関係ないイベントでも何度も state.request$ が流れてしまうので毎回初期化し上記 fetchIssues$ が最後のときだけ送られるようにする。
という感じで対応した。具体例は終わり。
Cycle.js は Rx.Observable を使った MVI を提案している。その最後に View が出すものは DOM (@cycle/dom) に向けてのものは virtual-dom が差分管理するのであまり気にしなくて良いのだけど、HTTP に向けてのものは気にしないといけないというのは辛い。一度しか送っちゃダメというのはなかなか難しい。
もっとうまくやれる気はする。