簡體   English   中英

ngrx - createSelector vs Observable.combineLatest

[英]ngrx - createSelector vs Observable.combineLatest

我剛剛遇到@ngrx自定義選擇器 ,我不能對這個功能感到驚訝。

根據他們對selectedUserbooks使用情況,我無法給出使用自定義選擇器的真正理由,例如:

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是重要組成部分,但據我了解,它不能執行這些性能提升到非原始值,因此它不會真正節省非原始片,通過使用任何計算可以解決RxcombineLatestdistinctUntilChanged運算符。

那么我錯過了什么,我為什么要使用@ngrx/selector

提前感謝任何見解。

也許除了記憶之外還有更多內容,但我沒有看到源代碼中突出的任何東西。 文檔中公布的所有內容都是memoization和重置它的方法,您基本上可以使用不同的運算符。 我會說使用它的原因是它很方便。 至少在簡單的情況下,它比將不同的運算符combineLatestcombineLatest每個輸入上更方便。

另一個好處是它允許您集中與您所在州的內部結構相關的邏輯。 您可以為它創建一個選擇器並執行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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM