Skip to content

Commit 35a4848

Browse files
timdeschryverbrandonroberts
authored andcommitted
feat(Store): createSelector with only a props selector
1 parent 53832a1 commit 35a4848

File tree

3 files changed

+87
-0
lines changed

3 files changed

+87
-0
lines changed

modules/store/spec/integration.spec.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,38 @@ describe('ngRx Integration spec', () => {
177177
});
178178

179179
it('should use props to get a todo', () => {
180+
const getTodosById = createSelector(
181+
(state: TodoAppSchema, id: number) => {
182+
return state.todos.find(p => p.id === id);
183+
}
184+
);
185+
186+
let testCase = 1;
187+
const todo$ = store.pipe(select(getTodosById, 2));
188+
todo$.subscribe(todo => {
189+
if (testCase === 1) {
190+
expect(todo).toEqual(undefined);
191+
} else if (testCase === 2) {
192+
expect(todo).toEqual({
193+
id: 2,
194+
text: 'second todo',
195+
completed: false,
196+
});
197+
} else if (testCase === 3) {
198+
expect(todo).toEqual({ id: 2, text: 'second todo', completed: true });
199+
}
200+
testCase++;
201+
});
202+
203+
store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } });
204+
store.dispatch({ type: ADD_TODO, payload: { text: 'second todo' } });
205+
store.dispatch({
206+
type: COMPLETE_TODO,
207+
payload: { id: 2 },
208+
});
209+
});
210+
211+
it('should use the selector and props to get a todo', () => {
180212
const getTodosState = createFeatureSelector<TodoAppSchema, Todo[]>(
181213
'todos'
182214
);

modules/store/spec/selector.spec.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,51 @@ describe('Selectors', () => {
224224
});
225225
});
226226

227+
describe('createSelector with only a props selector', () => {
228+
it('should deliver the state and the props to the projection function', () => {
229+
const projectFn = jasmine.createSpy('projectionFn');
230+
const state = { counter: {} };
231+
const props = { value: 47 };
232+
const selector = createSelector(projectFn)(state, props);
233+
expect(projectFn).toHaveBeenCalledWith(state, props);
234+
});
235+
236+
it('should be possible to use a projector fn', () => {
237+
const projectFn = jasmine.createSpy('projectionFn');
238+
const selector = createSelector(projectFn);
239+
selector.projector('foo', 'bar');
240+
expect(projectFn).toHaveBeenCalledWith('foo', 'bar');
241+
});
242+
243+
it('should call the projector function when the state changes', () => {
244+
const projectFn = jasmine.createSpy('projectionFn');
245+
const selector = createSelector(projectFn);
246+
247+
const firstState = { first: 'state' };
248+
const secondState = { second: 'state' };
249+
const props = { foo: 'props' };
250+
selector(firstState, props);
251+
selector(firstState, props);
252+
selector(secondState, props);
253+
expect(projectFn).toHaveBeenCalledTimes(2);
254+
});
255+
256+
it('should allow you to release memoized arguments', () => {
257+
const state = { first: 'state' };
258+
const props = { first: 'props' };
259+
const projectFn = jasmine.createSpy('projectionFn');
260+
const selector = createSelector(projectFn);
261+
262+
selector(state, props);
263+
selector(state, props);
264+
selector.release();
265+
selector(state, props);
266+
selector(state, props);
267+
268+
expect(projectFn).toHaveBeenCalledTimes(2);
269+
});
270+
});
271+
227272
describe('createSelector with arrays', () => {
228273
it('should deliver the value of selectors to the projection function', () => {
229274
const projectFn = jasmine.createSpy('projectionFn');

modules/store/src/selector.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,10 @@ export function createSelector<
435435
) => Result
436436
): MemoizedSelectorWithProps<State, Props, Result>;
437437

438+
export function createSelector<State, Props, Result>(
439+
projector: SelectorWithProps<State, Props, Result>
440+
): MemoizedSelectorWithProps<State, Props, Result>;
441+
438442
export function createSelector(
439443
...input: any[]
440444
): Selector<any, any> | SelectorWithProps<any, any, any> {
@@ -501,6 +505,12 @@ export function createSelectorFactory(
501505
});
502506

503507
const memoizedState = defaultMemoize(function(state: any, props: any) {
508+
// createSelector works directly on state
509+
// e.g. createSelector((state, props) => ...)
510+
if (selectors.length === 0) {
511+
return projector.apply(null, [state, props]);
512+
}
513+
504514
return options.stateFn.apply(null, [
505515
state,
506516
selectors,

0 commit comments

Comments
 (0)