[英]ngrx - createSelector vs Observable.combineLatest
我剛剛遇到@ngrx
的自定義選擇器 ,我不能對這個功能感到驚訝。
根據他們對selectedUser
的books
使用情況,我無法給出使用自定義選擇器的真正理由,例如:
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, selectAllBooks, (selectedUser: User, allBooks: Books[]) => {
return allBooks.filter((book: Book) => book.userId === selectedUser.id);
});
我試圖說服自己的記憶化了的createSelector
是重要組成部分,但據我了解,它不能執行這些性能提升到非原始值,因此它不會真正節省非原始片,通過使用任何計算可以解決Rx
與combineLatest
的distinctUntilChanged
運算符。
那么我錯過了什么,我為什么要使用@ngrx/selector
?
提前感謝任何見解。
也許除了記憶之外還有更多內容,但我沒有看到源代碼中突出的任何東西。 在文檔中公布的所有內容都是memoization和重置它的方法,您基本上可以使用不同的運算符。 我會說使用它的原因是它很方便。 至少在簡單的情況下,它比將不同的運算符combineLatest
到combineLatest
每個輸入上更方便。
另一個好處是它允許您集中與您所在州的內部結構相關的邏輯。 您可以為它創建一個選擇器並執行store.select(selectBaz)
,而不是在任何地方執行store.select(x => foo.bar.baz)
store.select(selectBaz)
。 您可以將選擇器組合到。 通過這種方式,您只需要設置邏輯以在一個位置遍歷狀態樹。 如果您必須更改狀態的結構,這是有益的,因為您只需要在一個地方進行更改而不是找到每個選擇器。 但是,每個人都可能不喜歡創建更多樣板文件。 但作為一個必須做國家重大改造的人,我只使用選擇器。
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)
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.