Skip to content

Commit

Permalink
feat(effects): add lazy withLatestFrom operator
Browse files Browse the repository at this point in the history
  • Loading branch information
david-shortman committed Oct 25, 2020
1 parent aa9bf1a commit 89238cd
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 0 deletions.
24 changes: 24 additions & 0 deletions modules/effects/spec/lazy_with_latest_from.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { of } from 'rxjs';
import { skipWhile, tap } from 'rxjs/operators';
import { hot } from 'jasmine-marbles';
import { lazyWithLatestFrom } from '../src/lazy_with_latest_from';

describe('lazyWithLatestFrom', () => {
it('should emit the combined values of the source and provided observables, and evaluate the array of observables only when the source emits a value', () => {
let evaluated = false;
const toBeLazilyEvaluated = () => {
evaluated = true;
return of(4);
};

const numbers$ = hot('abc', { a: 1, b: 2, c: 3 }).pipe(
skipWhile((number) => number < 3),
tap(() => expect(evaluated).toBe(false)),
lazyWithLatestFrom(() => [toBeLazilyEvaluated()])
);

expect(evaluated).toBe(false);
expect(numbers$).toBeObservable(hot('--d', { d: [3, 4] }));
expect(evaluated).toBe(true);
});
});
34 changes: 34 additions & 0 deletions modules/effects/src/lazy_with_latest_from.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Observable, of, OperatorFunction } from 'rxjs';
import { concatMap, withLatestFrom } from 'rxjs/operators';

/**
* @description
* RxJS operator that lazily evaluates a list of Observables.
*
* @param observables A function which returns an `Observable[]`.
* @returns Returns an `OperatorFunction` with ??? (idk how to describe this)
*
* @usageNotes
*
* ** Use to mitigate performance impact of `store.select` **
* ```ts
* effectName$ = createEffect(
* () => this.actions$.pipe(
* ofType(FeatureActions.actionOne),
* // The call to this.store.select will not be performed
* // until actionOne is received
* lazyWithLatestFrom(() => [this.store.select(getDataState)]),
* filter(([action, data]) => data.enabled),
* map(() => FeatureActions.actionTwo())
* )
* );
* ```
*/
export const lazyWithLatestFrom = <T extends Observable<unknown>[], V>(
observables: () => [...T]
) =>
concatMap((value) =>
of(value).pipe(withLatestFrom(...observables()))
) as OperatorFunction<V, [V, ...{ [i in keyof T]: TypeOfObservable<T[i]> }]>;

type TypeOfObservable<T> = T extends Observable<infer U> ? U : never;

0 comments on commit 89238cd

Please sign in to comment.