根据他们对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?
提前感谢任何见解.
另一个好处是它允许您集中与您所在州的内部结构相关的逻辑.您可以为其创建一个选择器并执行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 }) )) ); }