Skip to content

Commit da1ec80

Browse files
timdeschryverbrandonroberts
authored andcommitted
fix(Store): selector with only a projector (#1579)
Closes #1558 BREAKING CHANGE: Selectors with only a projector function aren't valid anymore. This change will make the usage more consistent. BEFORE: ``` const getTodosById = createSelector( (state: TodoAppSchema, id: number) => state.todos.find(p => p.id === id) ); ``` AFTER: ``` const getTodosById = createSelector( (state: TodoAppSchema) => state.todos, (todos: Todo[], id: number) => todos.find(p => p.id === id) ); ```
1 parent 5581826 commit da1ec80

File tree

3 files changed

+3
-165
lines changed

3 files changed

+3
-165
lines changed

modules/store/spec/integration.spec.ts

Lines changed: 3 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,9 @@ describe('ngRx Integration spec', () => {
179179

180180
it('should use props to get a todo', () => {
181181
const getTodosById = createSelector(
182-
(state: TodoAppSchema, id: number) => {
183-
return state.todos.find(p => p.id === id);
182+
(state: TodoAppSchema) => state.todos,
183+
(todos: Todo[], id: number) => {
184+
return todos.find(p => p.id === id);
184185
}
185186
);
186187

@@ -252,46 +253,6 @@ describe('ngRx Integration spec', () => {
252253
payload: { id: 2 },
253254
});
254255
});
255-
256-
it('should use the props in the projector to get a todo', () => {
257-
const getTodosState = createFeatureSelector<TodoAppSchema, Todo[]>(
258-
'todos'
259-
);
260-
261-
const getTodosById = createSelector(
262-
getTodosState,
263-
(todos: Todo[], { id }: { id: number }) =>
264-
todos.find(todo => todo.id === id)
265-
);
266-
267-
let testCase = 1;
268-
const todo$ = store.pipe(select(getTodosById, { id: 2 }));
269-
todo$.subscribe(todo => {
270-
if (testCase === 1) {
271-
expect(todo).toEqual(undefined);
272-
} else if (testCase === 2) {
273-
expect(todo).toEqual({
274-
id: 2,
275-
text: 'second todo',
276-
completed: false,
277-
});
278-
} else if (testCase === 3) {
279-
expect(todo).toEqual({
280-
id: 2,
281-
text: 'second todo',
282-
completed: true,
283-
});
284-
}
285-
testCase++;
286-
});
287-
288-
store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } });
289-
store.dispatch({ type: ADD_TODO, payload: { text: 'second todo' } });
290-
store.dispatch({
291-
type: COMPLETE_TODO,
292-
payload: { id: 2 },
293-
});
294-
});
295256
});
296257

297258
describe('using the select operator', () => {
@@ -357,42 +318,6 @@ describe('ngRx Integration spec', () => {
357318
expect(currentlyVisibleTodos.length).toBe(0);
358319
});
359320

360-
it('should use props to get a todo', () => {
361-
const getTodosById = createSelector(
362-
(state: TodoAppSchema, id: number) => {
363-
return state.todos.find(p => p.id === id);
364-
}
365-
);
366-
367-
let testCase = 1;
368-
const todo$ = store.pipe(select(getTodosById, 2));
369-
todo$.subscribe(todo => {
370-
if (testCase === 1) {
371-
expect(todo).toEqual(undefined);
372-
} else if (testCase === 2) {
373-
expect(todo).toEqual({
374-
id: 2,
375-
text: 'second todo',
376-
completed: false,
377-
});
378-
} else if (testCase === 3) {
379-
expect(todo).toEqual({
380-
id: 2,
381-
text: 'second todo',
382-
completed: true,
383-
});
384-
}
385-
testCase++;
386-
});
387-
388-
store.dispatch({ type: ADD_TODO, payload: { text: 'first todo' } });
389-
store.dispatch({ type: ADD_TODO, payload: { text: 'second todo' } });
390-
store.dispatch({
391-
type: COMPLETE_TODO,
392-
payload: { id: 2 },
393-
});
394-
});
395-
396321
it('should use the selector and props to get a todo', () => {
397322
const getTodosState = createFeatureSelector<TodoAppSchema, Todo[]>(
398323
'todos'

modules/store/spec/selector.spec.ts

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -143,17 +143,6 @@ describe('Selectors', () => {
143143
});
144144
});
145145

146-
it('should not short circuit to the projector fn if there are no selectors and props', () => {
147-
const projectFn = jasmine.createSpy('projectionFn');
148-
const state = { counter: {} };
149-
150-
const selector = (createSelector(projectFn) as any)(state);
151-
152-
// the projector still fires but without arguments,
153-
// this because there are no selectors and props
154-
expect(projectFn).toHaveBeenCalledWith();
155-
});
156-
157146
it('should be possible to test a projector fn independent from the selectors it is composed of', () => {
158147
const projectFn = jasmine.createSpy('projectionFn');
159148
const selector = createSelector(
@@ -239,51 +228,6 @@ describe('Selectors', () => {
239228
});
240229
});
241230

242-
describe('createSelector with only a props selector', () => {
243-
it('should deliver the state and the props to the projection function', () => {
244-
const projectFn = jasmine.createSpy('projectionFn');
245-
const state = { counter: {} };
246-
const props = { value: 47 };
247-
const selector = createSelector(projectFn)(state, props);
248-
expect(projectFn).toHaveBeenCalledWith(state, props);
249-
});
250-
251-
it('should be possible to use a projector fn', () => {
252-
const projectFn = jasmine.createSpy('projectionFn');
253-
const selector = createSelector(projectFn);
254-
selector.projector('foo', 'bar');
255-
expect(projectFn).toHaveBeenCalledWith('foo', 'bar');
256-
});
257-
258-
it('should call the projector function when the state changes', () => {
259-
const projectFn = jasmine.createSpy('projectionFn');
260-
const selector = createSelector(projectFn);
261-
262-
const firstState = { first: 'state' };
263-
const secondState = { second: 'state' };
264-
const props = { foo: 'props' };
265-
selector(firstState, props);
266-
selector(firstState, props);
267-
selector(secondState, props);
268-
expect(projectFn).toHaveBeenCalledTimes(2);
269-
});
270-
271-
it('should allow you to release memoized arguments', () => {
272-
const state = { first: 'state' };
273-
const props = { first: 'props' };
274-
const projectFn = jasmine.createSpy('projectionFn');
275-
const selector = createSelector(projectFn);
276-
277-
selector(state, props);
278-
selector(state, props);
279-
selector.release();
280-
selector(state, props);
281-
selector(state, props);
282-
283-
expect(projectFn).toHaveBeenCalledTimes(2);
284-
});
285-
});
286-
287231
describe('createSelector with arrays', () => {
288232
it('should deliver the value of selectors to the projection function', () => {
289233
const projectFn = jasmine.createSpy('projectionFn');

modules/store/src/selector.ts

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -472,31 +472,6 @@ export function createSelector<
472472
) => Result
473473
): MemoizedSelectorWithProps<State, Props, Result>;
474474

475-
/**
476-
* @deprecated
477-
* Selectors with only a projector function aren't valid anymore and will be removed in version 8.0.0
478-
*
479-
* BEFORE:
480-
*
481-
* ```ts
482-
* const getTodosById = createSelector(
483-
* (state: TodoAppSchema, id: number) => state.todos.find(p => p.id === id)
484-
* );
485-
* ```
486-
*
487-
* AFTER:
488-
*
489-
* ```ts
490-
* const getTodosById = createSelector(
491-
* (state: TodoAppSchema) => state.todos,
492-
* (todos: Todo[], id: number) => todos.find(p => p.id === id)
493-
* );
494-
* ```
495-
*/
496-
export function createSelector<State, Props, Result>(
497-
projector: SelectorWithProps<State, Props, Result>
498-
): MemoizedSelectorWithProps<State, Props, Result>;
499-
500475
export function createSelector(
501476
...input: any[]
502477
): Selector<any, any> | SelectorWithProps<any, any, any> {
@@ -570,12 +545,6 @@ export function createSelectorFactory(
570545
});
571546

572547
const memoizedState = defaultMemoize(function(state: any, props: any) {
573-
// createSelector works directly on state
574-
// e.g. createSelector((state, props) => ...)
575-
if (selectors.length === 0 && props !== undefined) {
576-
return projector.apply(null, [state, props]);
577-
}
578-
579548
return options.stateFn.apply(null, [
580549
state,
581550
selectors,

0 commit comments

Comments
 (0)