让我说我有一个可观察到的低级动作(例如,鼠标移动事件),并且我想同时扫描它们以构建一个状态,并发出一个更高的Observable - 级别动作(例如,鼠标手势)。如果我只使用scan(),则reducer只能返回状态,因此没有好处。如果我只在低级动作上使用map(),则投影函数无法访问状态,因此不会起作用。
我可以通过首先扫描()然后用原始动作流压缩它,或者让状态包含最后一个高级动作,然后使用map()来隔离高级别来解决它来自国家的行动,但那些都感觉有点粗暴。这似乎是一个相对简单的想法,所以人们会遵循一般模式吗?
(很明显,我实际上并没有使用鼠标手势,因此我不能像过去那样只看小窗口使用捷径:我真的想要完整状态。)
答案 0 :(得分:0)
我刚刚做的是scan
创建当前输入和状态的元组。然后,您就可以map
不再使用该状态。
例如它将所有内容包装在一个运算符中(TypeScript;如果坚持使用JavaScript,则删除类型):
export type FnWithState<F, S, T> = (from: F, state: S) => [T, S];
export function mapWithState<F, S, T>(fn: FnWithState<F, S, T>, initialState: S)
: OperatorFunction<F, T> {
return (in$: Rx.Observable<F>) =>
in$.pipe(scan(([_out, state]: [T, S], from: F) => fn(from, state),
[{} as T, initialState]),
map(([out, _state]) => out));
}
像这样使用它:
type WordHist = immutable.Map<string, number>;
function emitEveryThird(word: string, hist: WordHist): [string | null, WordHist] {
const newHist = hist.update(word, (n = 0) => n + 1);
return [newHist.get(word)! % 3 === 0 ? word : null, newHist];
}
const in$ = Rx.Observable.from(['a', 'b', 'a', 'a', 'a', 'b', 'a', 'c', 'a', 'b']);
const actual: string[] = [];
await in$.pipe(mapWithState(emitEveryThird, immutable.Map()))
.subscribe((w) => w && actual.push(w));
t.deepEqual(['a', 'a', 'b'], actual);