2

複数のイベント タイプに応じて変化するプロパティを Kefir で作成する慣用的な方法は何ですか?

私のプロジェクトではrxjs、FRP スタイルのアプリケーションに使用することから始めました。このアプリケーションでは、複数の変数に応じて変化する状態をサブスクライブしたいと考えました。これは私がそれを機能させた方法です:

const subject = new BehaviorSubject([]);

addEvents
  .withLatestFrom(subject, (newItem, items) => items.concat(newItem))
  .subscribe(subject);

removeEvents
  .withLatestFrom(subject, (item, items) => {
    return items.filter(i => i !== item);
  })
  .subscribe(subject);

これはおそらくベスト プラクティスではないことがわかりました。また、オブザーバーを複数のソースにサブスクライブすることは実際には正しくないこともわかりました。

多くの理由から、RxJs とは異なるライブラリーを試すことにしました。現在、ドキュメントが充実しており、パフォーマンスが優れていると思われる Kefir を評価しています。しかし、イベントタイプを検査する必要がある醜いハックを除けば、自分がやりたいことを行う方法を決定するのはさらに難しいと感じています:

kefir
  .merge(addEvents, removeEvents)
  .scan(
    (items, event) => {
      switch (event.type) {
        case 'add': return items.concat(event.item);
        case 'remove': return items.filter(i => i !== event.item);
      }
    },
    [])
  .toProperty();

一連の変更を作成するために、一連のイベント タイプに対して大きな条件付きブロックを使用するなどの洗練されていない手法を使用する必要はありません

Bacon.js を使用する予定はありませんが、まさに必要なものが含まれていることがわかります。

Bacon.update([],
  [addEvents],    (items, evt) => items.concat(evt.item),
  [removeEvents], (items, evt) => items.filter(i => i !== evt.item));

標準演算子を使用して Kefir でこの種のことを行う自然な方法はありますか、それとも自分で実装する必要があるのでしょうか?

4

1 に答える 1

4

私はわくわくしていないこのアプローチを見つけましたが、少なくともコードはかなりきれいです:

const items = function () {
  let items = [];  
  return kefir
    .merge([
      addEvents.map(item => items = items.concat(item)),
      removeEvents.map(item => items = items.filter(i => i !== item))
    ])
    .toProperty(() => items);
}();

これについて私が気に入らないのは、状態を変更することですが、JavaScript はシングル スレッドであり、その状態を隠しているので、それほど悪くないのかもしれません。

これをユーティリティ関数として示します。

import kefir from 'kefir';

export default function dynamicValue(initValue, ...args) {
  let value = initValue;
  let streams = [];
  while (args.length) {
    let [source, xform, ...remaining] = args;
    streams.push(source.map(v => value = xform(value, v)));
    args = remaining;
  }

  return kefir.merge(streams).toProperty(() => value);
}

...次のように使用されます:

dynamicValue([],
  addEvents, (items, item) => items.concat(item),
  removeEvents, (items, item) => items.filter(i => i !== item));

更新

変数を変更する必要なく、 、、およびをdynamicValue使用して実装する別の方法を考え出しました。mapmergescan

import kefir from 'kefir';

export default function transform(initValue, ...args) {
  let mutations = [];
  while (args.length) {
    let [source, calculateNewValue, ...newArgs] = args;
    mutations.push(source.map(e => ({ event: e, mutation: calculateNewValue })));
    args = newArgs;
  }

  return kefir
    .merge(mutations)
    .scan((prev, { event, mutation }) => mutation(prev, event), initValue);
}

基本的に、各イベントをミューテーション関数とペアにし、これらのペアをマージしてスキャンし、ミューテーション関数を元の値とイベントに適用します。本当に優れているかどうかはわかりませんが、より「機能的」に見えます。

于 2015-08-02T05:02:34.327 に答える