angular – ngrx – createSelector vs Observable.combineLatest

我刚刚遇到@ngrx的 custom selectors,我简直不能对这个功能感到惊讶.

根据他们对selectedUser的书籍使用情况,我无法给出使用自定义选择器的真正理由,例如:

export const selectVisibleBooks = createSelector(selectUser,selectAllBooks,(selectedUser: User,allBooks: Books[]) => {
    return allBooks.filter((book: Book) => book.userId === selectedUser.id);
});

而不是像:

export const selectVisibleBooks = Observable.combineLatest(selectUser,allBooks: Books[]) => {
    return allBooks.filter((book: Book) => book.userId === selectedUser.id);
});

我试图说服自己,createSelector的memoization是关键部分,但据我所知,它不能对非原始值执行这些性能提升,因此它不会真正保存非原始切片的任何计算,这通过使用可以解决具有combineLatest的Rx distinctUntilChanged运算符.

那么我错过了什么,我为什么要使用@ ngrx / selector?

提前感谢任何见解.

也许除了记忆之外还有更多内容,但我没有看到 source code中突出的任何东西.在 docs中宣传的所有内容都是memoization和重置它的方法,你基本上也可以使用不同的运算符.我会说使用它的原因是它很方便.至少在简单的情况下,它比将不同的运算符绑定到combineLatest的每个输入上更方便.

一个好处是它允许您集中与您所在州的内部结构相关的逻辑.您可以为其创建一个选择器并执行store.select(selectBaz),而不是在任何地方执行store.select(x => foo.bar.baz).您可以将选择器组合到.通过这种方式,您只需要设置逻辑以在一个位置遍历状态树.如果您必须更改状态的结构,这是有益的,因为您只需要在一个地方进行更改而不是找到每个选择器.但是,每个人都可能不喜欢创建更多样板文件.但作为一个必须做国家重大改造的人,我只使用选择器.

createSelector非常基本,所以你只能将它用于基本的操作.在您检索仅需要过滤子集的对象列表的情况下,它不足.这是一个例子:

const selectParentVmById = (id: string) => createSelector<RootState,Parent,Child[],ParentVm>(
    selectParentById(id),selectChildren(),(parent: Parent,children: Child[]) => (<ParentVm>{
        ...parent,children: children.filter(child => parent.children.includes(child.id))
    })
);

在这种情况下,选择器selectParentVmById将在selectChildren()发出不同的数组时发出,如果其中的任何元素发生更改,则会发生这种情况.如果更改的元素是父项的子项之一,那么这很好.如果不是那么你会得到不必要的流失,因为memoization是在整个列表而不是筛选列表(或者更确切地说是其中的元素)上完成的.我有很多这样的场景,并且只使用createSelector用于简单的选择器,并将它们与combineLatest组合并滚动我自己的memoization.

这不是一般不使用它的理由,你只需要知道它的局限性.

额外信用

你的问题不是关于这一点,但是自从我提出这个问题后,我认为我会给出完整性的解决方案.我开始使用一个名为distinctElements()的自定义运算符,它的作用类似于distinctUntilChanged(),但是应用于列表中的元素而不是列表本身.

这是操作符:

import { Observable } from 'rxjs/Observable';
import { startWith,pairwise,filter,map } from 'rxjs/operators';

export const distinctElements = () => <T extends Array<V>,V>(source: Observable<T>) => {
    return source.pipe(
        startWith(<T>null),pairwise(),filter(([a,b]) => a == null || a.length !== b.length || a.some(x => !b.includes(x))),map(([a,b]) => b)
    )
};

以下是重构使用它的上述代码

const selectParentVmById = (store: Store<RootState>,id: string): ParentVm => {
    return store.select(selectParentById(id)).pipe(
        distinctUntilChanged(),switchMap((parent) => store.select(selectChildren()).pipe(
            map((children) => children.filter(child => parent.children.includes(child.id))),distinctElements(),map((children) => <ParentVm> { ...parent,children })
        ))
    );
}

需要更多的代码,但它削减了浪费的工作.您可以根据您的方案添加shareReplay(1).

相关文章

ANGULAR.JS:NG-SELECTANDNG-OPTIONSPS:其实看英文文档比看中...
AngularJS中使用Chart.js制折线图与饼图实例  Chart.js 是...
IE浏览器兼容性后续前言 继续尝试解决IE浏览器兼容性问题,...
Angular实现下拉菜单多选写这篇文章时,引用文章地址如下:h...
在AngularJS应用中集成科大讯飞语音输入功能前言 根据项目...
Angular数据更新不及时问题探讨前言 在修复控制角标正确变...