Skip to content

Commit

Permalink
update to support passing single observable in factory
Browse files Browse the repository at this point in the history
  • Loading branch information
david-shortman committed Oct 27, 2020
1 parent 093715b commit a6b2d22
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 56 deletions.
47 changes: 47 additions & 0 deletions modules/effects/spec/concat_latest_from.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { of } from 'rxjs';
import { skipWhile, tap } from 'rxjs/operators';
import { hot } from 'jasmine-marbles';
import { concatLatestFrom } from '../src/concat_latest_from';

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

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

// then
expect(evaluated).toBe(false);
expect(numbers$).toBeObservable(hot('--d', { d: [3, 4] }));
expect(evaluated).toBe(true);
});
it('should evaluate the observable only when the source emits a value', () => {
// given
let evaluated = false;
const toBeLazilyEvaluated = () => {
evaluated = true;
return of(4);
};

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

// then
expect(evaluated).toBe(false);
expect(numbers$).toBeObservable(hot('--d', { d: [3, 4] }));
expect(evaluated).toBe(true);
});
});
24 changes: 0 additions & 24 deletions modules/effects/spec/lazy_with_latest_from.spec.ts

This file was deleted.

80 changes: 80 additions & 0 deletions modules/effects/src/concat_latest_from.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Observable, of, OperatorFunction } from 'rxjs';
import { concatMap, withLatestFrom } from 'rxjs/operators';

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

/**
* @description
* RxJS operator that lazily evaluates a list of Observables.
*
* @param observablesFactory 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 function concatLatestFrom<T extends Observable<unknown>[], V>(
observablesFactory: (value: V) => [...T]
): OperatorFunction<V, [V, ...{ [i in keyof T]: TypeOfObservable<T[i]> }]>;
/**
* @description
* RxJS operator that lazily evaluates an Observable.
*
* @param observableFactory 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 function concatLatestFrom<T extends Observable<unknown>, V>(
observableFactory: (value: V) => T
): OperatorFunction<V, [V, TypeOfObservable<T>]>;

export function concatLatestFrom<
T extends Observable<unknown>[] | Observable<unknown>,
V
>(
observablesFactory: (value: V) => T extends Observable<unknown>[] ? [...T] : T
) {
return concatMap((value) => {
const observables = observablesFactory(value);
let observablesAsArray = Array.isArray(observables)
? observables
: [observables];
return of(value).pipe(withLatestFrom(...observablesAsArray));
}) as OperatorFunction<
V,
[
V,
...(T extends Observable<unknown>[]
? { [i in keyof T]: TypeOfObservable<T[i]> }
: [TypeOfObservable<T>])
]
>;
}
32 changes: 0 additions & 32 deletions modules/effects/src/lazy_with_latest_from.ts

This file was deleted.

0 comments on commit a6b2d22

Please sign in to comment.