使用 Kefir 用多个事件改变 属性 的惯用方法
Idiomatic way to mutate a property with multiple events using Kefir
在 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 的标准运算符来完成此类操作,或者这是我最终不得不自己实现的方法吗?
我想出了这种方法,但我并不喜欢这种方法,但至少代码非常干净:
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));
更新:
想出一种不同的方法来使用 map
、merge
和 scan
来实现 dynamicValue
,而无需改变变量:
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);
}
基本上,它将每个事件与突变函数配对,合并这些对,然后扫描它们,将突变函数应用于原始值和事件。我不确定它是否真的更好,但它确实看起来更 "functional."
在 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 的标准运算符来完成此类操作,或者这是我最终不得不自己实现的方法吗?
我想出了这种方法,但我并不喜欢这种方法,但至少代码非常干净:
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));
更新:
想出一种不同的方法来使用 map
、merge
和 scan
来实现 dynamicValue
,而无需改变变量:
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);
}
基本上,它将每个事件与突变函数配对,合并这些对,然后扫描它们,将突变函数应用于原始值和事件。我不确定它是否真的更好,但它确实看起来更 "functional."