Skip to content

Commit

Permalink
feat(store): provide props to createSelector projector function (#1210)
Browse files Browse the repository at this point in the history
  • Loading branch information
timdeschryver authored and brandonroberts committed Jul 30, 2018
1 parent 4a0b580 commit b1f9b34
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 21 deletions.
36 changes: 36 additions & 0 deletions modules/store/spec/integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,42 @@ describe('ngRx Integration spec', () => {
payload: { id: 2 },
});
});

it('should use the props in the projector to get a todo', () => {
const getTodosState = createFeatureSelector<TodoAppSchema, Todo[]>(
'todos'
);

const getTodosById = createSelector(
getTodosState,
(todos: Todo[], { id }: { id: number }) =>
todos.find(todo => todo.id === id)
);

let testCase = 1;
const todo$ = store.pipe(select(getTodosById, { id: 2 }));
todo$.subscribe(todo => {
if (testCase === 1) {
expect(todo).toEqual(undefined);
} else if (testCase === 2) {
expect(todo).toEqual({
id: 2,
text: 'second todo',
completed: false,
});
} else if (testCase === 3) {
expect(todo).toEqual({ id: 2, text: 'second todo', completed: true });
}
testCase++;
});

store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } });
store.dispatch({ type: ADD_TODO, payload: { text: 'second todo' } });
store.dispatch({
type: COMPLETE_TODO,
payload: { id: 2 },
});
});
});

describe('feature state', () => {
Expand Down
8 changes: 6 additions & 2 deletions modules/store/spec/selector.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,9 @@ describe('Selectors', () => {
);

selector({}, { value: 47 });
expect(projectFn).toHaveBeenCalledWith(countOne, countTwo, 47);
expect(projectFn).toHaveBeenCalledWith(countOne, countTwo, 47, {
value: 47,
});
});

it('should be possible to test a projector fn independent from the selectors it is composed of', () => {
Expand Down Expand Up @@ -363,7 +365,9 @@ describe('Selectors', () => {
projectFn
)({}, { value: 47 });

expect(projectFn).toHaveBeenCalledWith(countOne, countTwo, 47);
expect(projectFn).toHaveBeenCalledWith(countOne, countTwo, 47, {
value: 47,
});
});

it('should be possible to test a projector fn independent from the selectors it is composed of', () => {
Expand Down
81 changes: 62 additions & 19 deletions modules/store/src/selector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,15 @@ export function createSelector<State, S1, Result>(
): MemoizedSelector<State, Result>;
export function createSelector<State, Props, S1, Result>(
s1: SelectorWithProps<State, Props, S1>,
projector: (s1: S1) => Result
projector: (s1: S1, props: Props) => Result
): MemoizedSelectorWithProps<State, Props, Result>;
export function createSelector<State, S1, Result>(
selectors: [Selector<State, S1>],
projector: (s1: S1) => Result
): MemoizedSelector<State, Result>;
export function createSelector<State, Props, S1, Result>(
selectors: [SelectorWithProps<State, Props, S1>],
projector: (s1: S1) => Result
projector: (s1: S1, props: Props) => Result
): MemoizedSelectorWithProps<State, Props, Result>;

export function createSelector<State, S1, S2, Result>(
Expand All @@ -109,7 +109,7 @@ export function createSelector<State, S1, S2, Result>(
export function createSelector<State, Props, S1, S2, Result>(
s1: SelectorWithProps<State, Props, S1>,
s2: SelectorWithProps<State, Props, S2>,
projector: (s1: S1, s2: S2) => Result
projector: (s1: S1, s2: S2, props: Props) => Result
): MemoizedSelectorWithProps<State, Props, Result>;
export function createSelector<State, S1, S2, Result>(
selectors: [Selector<State, S1>, Selector<State, S2>],
Expand All @@ -120,7 +120,7 @@ export function createSelector<State, Props, S1, S2, Result>(
SelectorWithProps<State, Props, S1>,
SelectorWithProps<State, Props, S2>
],
projector: (s1: S1, s2: S2) => Result
projector: (s1: S1, s2: S2, props: Props) => Result
): MemoizedSelectorWithProps<State, Props, Result>;

export function createSelector<State, S1, S2, S3, Result>(
Expand All @@ -133,7 +133,7 @@ export function createSelector<State, Props, S1, S2, S3, Result>(
s1: SelectorWithProps<State, Props, S1>,
s2: SelectorWithProps<State, Props, S2>,
s3: SelectorWithProps<State, Props, S3>,
projector: (s1: S1, s2: S2, s3: S3) => Result
projector: (s1: S1, s2: S2, s3: S3, props: Props) => Result
): MemoizedSelectorWithProps<State, Props, Result>;
export function createSelector<State, S1, S2, S3, Result>(
selectors: [Selector<State, S1>, Selector<State, S2>, Selector<State, S3>],
Expand All @@ -145,7 +145,7 @@ export function createSelector<State, Props, S1, S2, S3, Result>(
SelectorWithProps<State, Props, S2>,
SelectorWithProps<State, Props, S3>
],
projector: (s1: S1, s2: S2, s3: S3) => Result
projector: (s1: S1, s2: S2, s3: S3, props: Props) => Result
): MemoizedSelectorWithProps<State, Props, Result>;

export function createSelector<State, S1, S2, S3, S4, Result>(
Expand All @@ -160,7 +160,7 @@ export function createSelector<State, Props, S1, S2, S3, S4, Result>(
s2: SelectorWithProps<State, Props, S2>,
s3: SelectorWithProps<State, Props, S3>,
s4: SelectorWithProps<State, Props, S4>,
projector: (s1: S1, s2: S2, s3: S3, s4: S4) => Result
projector: (s1: S1, s2: S2, s3: S3, s4: S4, props: Props) => Result
): MemoizedSelectorWithProps<State, Props, Result>;
export function createSelector<State, S1, S2, S3, S4, Result>(
selectors: [
Expand All @@ -178,7 +178,7 @@ export function createSelector<State, Props, S1, S2, S3, S4, Result>(
SelectorWithProps<State, Props, S3>,
SelectorWithProps<State, Props, S4>
],
projector: (s1: S1, s2: S2, s3: S3, s4: S4) => Result
projector: (s1: S1, s2: S2, s3: S3, s4: S4, props: Props) => Result
): MemoizedSelectorWithProps<State, Props, Result>;

export function createSelector<State, S1, S2, S3, S4, S5, Result>(
Expand All @@ -195,7 +195,7 @@ export function createSelector<State, Props, S1, S2, S3, S4, S5, Result>(
s3: SelectorWithProps<State, Props, S3>,
s4: SelectorWithProps<State, Props, S4>,
s5: SelectorWithProps<State, Props, S5>,
projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5) => Result
projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, props: Props) => Result
): MemoizedSelectorWithProps<State, Props, Result>;
export function createSelector<State, S1, S2, S3, S4, S5, Result>(
selectors: [
Expand All @@ -215,7 +215,7 @@ export function createSelector<State, Props, S1, S2, S3, S4, S5, Result>(
SelectorWithProps<State, Props, S4>,
SelectorWithProps<State, Props, S5>
],
projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5) => Result
projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, props: Props) => Result
): MemoizedSelectorWithProps<State, Props, Result>;

export function createSelector<State, S1, S2, S3, S4, S5, S6, Result>(
Expand All @@ -234,7 +234,15 @@ export function createSelector<State, Props, S1, S2, S3, S4, S5, S6, Result>(
s4: SelectorWithProps<State, Props, S4>,
s5: SelectorWithProps<State, Props, S5>,
s6: SelectorWithProps<State, Props, S6>,
projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6) => Result
projector: (
s1: S1,
s2: S2,
s3: S3,
s4: S4,
s5: S5,
s6: S6,
props: Props
) => Result
): MemoizedSelectorWithProps<State, Props, Result>;
export function createSelector<State, S1, S2, S3, S4, S5, S6, Result>(
selectors: [
Expand All @@ -256,7 +264,15 @@ export function createSelector<State, Props, S1, S2, S3, S4, S5, S6, Result>(
SelectorWithProps<State, Props, S5>,
SelectorWithProps<State, Props, S6>
],
projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6) => Result
projector: (
s1: S1,
s2: S2,
s3: S3,
s4: S4,
s5: S5,
s6: S6,
props: Props
) => Result
): MemoizedSelectorWithProps<State, Props, Result>;

export function createSelector<State, S1, S2, S3, S4, S5, S6, S7, Result>(
Expand Down Expand Up @@ -288,7 +304,16 @@ export function createSelector<
s5: SelectorWithProps<State, Props, S5>,
s6: SelectorWithProps<State, Props, S6>,
s7: SelectorWithProps<State, Props, S7>,
projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7) => Result
projector: (
s1: S1,
s2: S2,
s3: S3,
s4: S4,
s5: S5,
s6: S6,
s7: S7,
props: Props
) => Result
): MemoizedSelectorWithProps<State, Props, Result>;
export function createSelector<State, S1, S2, S3, S4, S5, S6, S7, Result>(
selectors: [
Expand Down Expand Up @@ -323,7 +348,16 @@ export function createSelector<
SelectorWithProps<State, Props, S6>,
SelectorWithProps<State, Props, S7>
],
projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7) => Result
projector: (
s1: S1,
s2: S2,
s3: S3,
s4: S4,
s5: S5,
s6: S6,
s7: S7,
props: Props
) => Result
): MemoizedSelectorWithProps<State, Props, Result>;

export function createSelector<State, S1, S2, S3, S4, S5, S6, S7, S8, Result>(
Expand Down Expand Up @@ -375,7 +409,8 @@ export function createSelector<
s5: S5,
s6: S6,
s7: S7,
s8: S8
s8: S8,
props: Props
) => Result
): MemoizedSelectorWithProps<State, Props, Result>;
export function createSelector<State, S1, S2, S3, S4, S5, S6, S7, S8, Result>(
Expand Down Expand Up @@ -431,7 +466,8 @@ export function createSelector<
s5: S5,
s6: S6,
s7: S7,
s8: S8
s8: S8,
props: Props
) => Result
): MemoizedSelectorWithProps<State, Props, Result>;

Expand All @@ -447,12 +483,19 @@ export function createSelector(

export function defaultStateFn(
state: any,
selectors: any[],
selectors: Selector<any, any>[] | SelectorWithProps<any, any, any>[],
props: any,
memoizedProjector: MemoizedProjection
): any {
const args = selectors.map(fn => fn(state, props));
return memoizedProjector.memoized.apply(null, args);
if (props === undefined) {
const args = (<Selector<any, any>[]>selectors).map(fn => fn(state));
return memoizedProjector.memoized.apply(null, args);
}

const args = (<SelectorWithProps<any, any, any>[]>selectors).map(fn =>
fn(state, props)
);
return memoizedProjector.memoized.apply(null, [...args, props]);
}

export type SelectorFactoryConfig<T = any, V = any> = {
Expand Down

0 comments on commit b1f9b34

Please sign in to comment.