Skip to content

Commit

Permalink
feat(app-state): spreadArrayStore$() handles null and undefined
Browse files Browse the repository at this point in the history
Closes #4
  • Loading branch information
ersimont committed Nov 23, 2020
1 parent 8e9bec9 commit 16e0dce
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 15 deletions.
40 changes: 29 additions & 11 deletions projects/app-state/src/lib/utils/spread-array-store.spec.ts
Expand Up @@ -11,26 +11,26 @@ describe('spreadArrayStore$()', () => {

it('emits a separate store object for each element in the array', () => {
store.set([1, 2]);
let emitted: Array<Store<number>>;
let emitted!: Array<Store<number>>;
spreadArrayStore$(store).subscribe((stores) => {
emitted = stores;
});
expect(emitted!.length).toBe(2);
expect(emitted![0].state()).toBe(1);
expect(emitted![1].state()).toBe(2);
expect(emitted.length).toBe(2);
expect(emitted[0].state()).toBe(1);
expect(emitted[1].state()).toBe(2);

store.set([3, 4, 5]);
expect(emitted!.length).toBe(3);
expect(emitted![0].state()).toBe(3);
expect(emitted![1].state()).toBe(4);
expect(emitted![2].state()).toBe(5);
expect(emitted.length).toBe(3);
expect(emitted[0].state()).toBe(3);
expect(emitted[1].state()).toBe(4);
expect(emitted[2].state()).toBe(5);

store.set([6]);
expect(emitted!.length).toBe(1);
expect(emitted![0].state()).toBe(6);
expect(emitted.length).toBe(1);
expect(emitted[0].state()).toBe(6);

store.set([]);
expect(emitted!.length).toBe(0);
expect(emitted.length).toBe(0);
});

it('only emits when the length of the array changes', () => {
Expand Down Expand Up @@ -63,4 +63,22 @@ describe('spreadArrayStore$()', () => {
store.set([6]);
expect(lastEmit![0]).toBe(previousEmit![0]);
});

it('treats null and undefined as empty arrays', () => {
interface State {
array?: number[] | null;
}
const arrayStore = new RootStore<State>({})('array');
let emitted!: Array<Store<number>>;
spreadArrayStore$(arrayStore).subscribe((stores) => {
emitted = stores;
});
expect(emitted.length).toBe(0);

arrayStore.set([1]);
expect(emitted.length).toBe(1);

arrayStore.delete();
expect(emitted.length).toBe(0);
});
});
9 changes: 5 additions & 4 deletions projects/app-state/src/lib/utils/spread-array-store.ts
@@ -1,5 +1,5 @@
import { Observable } from 'rxjs';
import { distinctUntilKeyChanged, map } from 'rxjs/operators';
import { distinctUntilChanged, map } from 'rxjs/operators';
import { Store } from '../index';

/**
Expand All @@ -25,18 +25,19 @@ import { Store } from '../index';
* ```
*/
export function spreadArrayStore$<T>(
source: Store<Array<T>>,
source: Store<Array<T> | null | undefined>,
): Observable<Array<Store<T>>> {
let cache: Array<Store<T>> = [];
return source.$.pipe(
distinctUntilKeyChanged('length'),
distinctUntilChanged((x, y) => (x?.length || 0) === (y?.length || 0)),
map((array) => {
array ??= [];
if (array.length < cache.length) {
cache = cache.slice(0, array.length);
} else {
cache = cache.slice();
for (let i = cache.length; i < array.length; ++i) {
cache[i] = source(i);
cache[i] = (source as Store<Array<T>>)(i);
}
}
return cache;
Expand Down
15 changes: 15 additions & 0 deletions projects/app-state/src/typing-tests/spead-array-store.dts-spec.ts
@@ -0,0 +1,15 @@
import { spreadArrayStore$, Store } from '../public-api';

declare const array: Store<Array<number>>;
declare const arrayOrNull: Store<Array<number> | null>;
declare const arrayOrUndefined: Store<Array<number> | undefined>;
declare const arrayOrNil: Store<Array<number> | null | undefined>;

// $ExpectType Observable<Store<number>[]>
spreadArrayStore$(array);
// $ExpectType Observable<Store<number>[]>
spreadArrayStore$(arrayOrNull);
// $ExpectType Observable<Store<number>[]>
spreadArrayStore$(arrayOrUndefined);
// $ExpectType Observable<Store<number>[]>
spreadArrayStore$(arrayOrNil);

0 comments on commit 16e0dce

Please sign in to comment.