What are the different between select with ngrx and rxjs if I have combineLatest?

Viewed 163

I have product service and I want to get the best product from the state.

One way I can achieve this is by using rxjs like so:

 @Injectable({ providedIn: 'root' })
 export class ProductDataService extends EntityCollectionServiceBase<Product> {
  
    bestProductsIds$ = new BehaviorSubject([]);

    bestProducts$ = combineLatest([this.entities$, this.bestProductsIds$]).pipe(
      map(([products, ids]) => products.filter((p) => ids.includes(p.id))),
      map((products) => orderBy(product, ['createdDate', ['desc']])),
      map((products) => this.toProductCard(products))
    );
}

Another way to do it is with selectors:

export const selectBestProducts = createSelector(
  ([products, ids]) => products.filter((p) => ids.includes(p.id)),
  (products) => orderBy(products, ['createdDate', ['desc']])
);


@Injectable({ providedIn: 'root' })
export class ProductDataService extends EntityCollectionServiceBase<Product> {

  bestProductsIds$ = new BehaviorSubject([]);

  bestProducts$ = combineLatest([this.entities$, this.bestProductsIds$]).pipe(select(selectBestProducts));

  ...
}

I know the ngrx selectors are memoized functions but I'm using here combineLatest.

So what are the different between the two approaches in this case? or it's the same?

1 Answers

The short answer is there are no differences, as in the case with combineLatest you are using two selectors that have memoized values under the hood.

Long answer about the differences, if we go to the selector implementation inside the ngrx Docs we can see that the createSelector functions is using defaultMemoize to track any changes which is using isEqualCheck that looks like so in the end

export function isEqualCheck(a: any, b: any): boolean {
  return a === b;
}

so whenever a filed in the state is changed (field that we are depending on inside the selector) the related selector will compute it's new value. The interesting thing that you can do with selectors is to use createSeletorFactory where you can provide your own custom compare function, for further optimization when needed.

About combineLatest usage with selectors, you have pretty much the same effect, once all observables had emitted values and evrytime when new value is passed from any of those observables, new value will be emitter further down the stream, that will be processed by your logic (the part about fining the most popular items), here you can achieve the same optimization effects by using the distincUntilChanged operator again with your own custom compare.

In summary both approaches behave the same way, the only difference is that in the first case the selector is doing the required computations and in the second case the computation is handled inside the stream emitted from the combineLatest observable.

Related