我有一个在呈现虚拟列表中的最后一项时触发onScrollEnd
事件的组件。此事件将发出一个新的API请求,以获取下一页,并使用scan
运算符将它们与以前的结果合并。
此组件还具有一个触发onSearch
事件的搜索字段。
触发搜索事件后,如何从scan
运算符中清除以前的累积结果?还是我需要在这里重构逻辑?
const loading$ = new BehaviorSubject(false);
const offset$ = new BehaviorSubject(0);
const search$ = new BehaviorSubject(null);
const options$: Observable<any[]> = merge(offset$, search$).pipe(
// 1. Start the loading indicator.
tap(() => loading$.next(true)),
// 2. Fetch new items based on the offset.
switchMap(([offset, searchterm]) => userService.getUsers(offset, searchterm)),
// 3. Stop the loading indicator.
tap(() => loading$.next(false)),
// 4. Complete the Observable when there is no 'next' link.
takeWhile((response) => response.links.next),
// 5. Map the response.
map(({ data }) =>
data.map((user) => ({
label: user.name,
value: user.id
}))
),
// 6. Accumulate the new options with the previous options.
scan((acc, curr) => {
// TODO: Dont merge on search$.next
return [...acc, ...curr]);
}
);
// Fetch next page
onScrollEnd: (offset: number) => offset$.next(offset);
// Fetch search results
onSearch: (term) => {
search$.next(term);
};
答案 0 :(得分:1)
这是一个有趣的流。考虑一下,offset $和search $实际上是两个单独的流,但是逻辑不同,因此应该在最后而不是开始合并。
此外,在我看来,搜索应将偏移量重置为0,而在当前逻辑中我看不到。
所以这是我的主意:
const offsettedOptions$ = offset$.pipe(
tap(() => loading$.next(true)),
withLatestFrom(search$),
concatMap(([offset, searchterm]) => userService.getUsers(offset, searchterm)),
tap(() => loading$.next(false)),
map(({ data }) =>
data.map((user) => ({
label: user.name,
value: user.id
})),
scan((acc, curr) => [...acc, ...curr])
);
const searchedOptions$ = search$.pipe(
tap(() => loading$.next(true)),
concatMap(searchTerm => userService.getUsers(0, searchterm)),
tap(() => loading$.next(false)),
map(({ data }) =>
data.map((user) => ({
label: user.name,
value: user.id
})),
);
const options$ = merge(offsettedOptions, searchedOptions);
查看是否可行或有意义。我可能缺少一些背景信息。
答案 1 :(得分:1)
我认为您可以通过重组链来实现您想要的目标(为简化起见,我省略了tap
调用来触发加载):
search$.pipe(
switchMap(searchterm =>
concat(
userService.getUsers(0, searchterm),
offset$.pipe(concatMap(offset => userService.getUsers(offset, searchterm)))),
).pipe(
map(({ data }) => data.map((user) => ({
label: user.name,
value: user.id
}))),
scan((acc, curr) => [...acc, ...curr], []),
),
),
);
search$
的每个发射都将创建一个具有自己的scan
的新内部Observable,该内部Observable将从一个空的累加器开始。
答案 2 :(得分:1)
要操作 state
的 scan
,您可以编写 higher order functions 来获取旧状态和新更新。然后结合 merge 运算符
const { Subject, merge } = rxjs;
const { scan, map } = rxjs.operators;
add$ = new Subject();
clear$ = new Subject();
add = (value) => (state) => [...state, value];
clear = () => (state) => [];
const result$ = merge(
add$.pipe(map(add)),
clear$.pipe(map(clear))
).pipe(
scan((state, innerFn) => innerFn(state), [])
)
result$.subscribe(console.log)
add$.next(1)
add$.next(2)
clear$.next()
add$.next(3)
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js"></script>
答案 3 :(得分:0)
找到了可行的解决方案:我在withLatestFrom
运算符之前使用scan
检查当前偏移量,并根据需要根据该值重置累加器。