Skip to content

Commit 918f184

Browse files
perf(router-store): optimize selectQueryParams, selectQueryParam and selectFragment selectors (#2764)
BREAKING CHANGE: Router-store selectors for query params and fragment select from the root router state node. This could potentially break unit tests, but is functionally equivalent to the current behavior at runtime. BEFORE: selectQueryParams - returns query params from the last router state node selectQueryParam - returns a query param from the last router state node selectFragment - returns the fragment from the last router state node AFTER: selectQueryParams - returns query params from routerState.root selectQueryParam - returns a query param from routerState.root selectFragment - returns the fragment from routerState.root
1 parent 7c29320 commit 918f184

File tree

3 files changed

+97
-33
lines changed

3 files changed

+97
-33
lines changed

modules/router-store/spec/router_selectors.spec.ts

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@ const mockData = {
1212
url: [],
1313
outlet: 'primary',
1414
routeConfig: null,
15-
queryParams: {},
15+
queryParams: {
16+
ref: 'ngrx.io',
17+
},
1618
queryParamMap: {
17-
params: {},
19+
params: {
20+
ref: 'ngrx.io',
21+
},
1822
},
19-
fragment: null,
23+
fragment: 'test-fragment',
2024
firstChild: {
2125
params: {},
2226
paramMap: {
@@ -34,10 +38,12 @@ const mockData = {
3438
path: 'login',
3539
},
3640
queryParams: {
37-
id: 3,
41+
ref: 'ngrx.io',
3842
},
3943
queryParamMap: {
40-
params: {},
44+
params: {
45+
ref: 'ngrx.io',
46+
},
4147
},
4248
firstChild: {
4349
params: {
@@ -72,7 +78,7 @@ const mockData = {
7278
fragment: 'test-fragment',
7379
children: [],
7480
},
75-
fragment: null,
81+
fragment: 'test-fragment',
7682
children: [],
7783
},
7884
children: [
@@ -92,10 +98,15 @@ const mockData = {
9298
routeConfig: {
9399
path: 'login',
94100
},
95-
queryParams: {},
101+
queryParams: {
102+
ref: 'ngrx.io',
103+
},
96104
queryParamMap: {
97-
params: {},
105+
params: {
106+
ref: 'ngrx.io',
107+
},
98108
},
109+
fragment: 'test-fragment',
99110
children: [],
100111
},
101112
],
@@ -104,6 +115,7 @@ const mockData = {
104115
},
105116
navigationId: 1,
106117
};
118+
107119
describe('Router State Selectors', () => {
108120
describe('Composed Selectors', () => {
109121
interface State {
@@ -145,25 +157,19 @@ describe('Router State Selectors', () => {
145157
it('should create a selector for selecting the fragment', () => {
146158
const result = selectors.selectFragment(state);
147159

148-
expect(result).toEqual(
149-
state.router.state.root.firstChild.firstChild.fragment
150-
);
160+
expect(result).toEqual(state.router.state.root.fragment);
151161
});
152162

153163
it('should create a selector for selecting the query params', () => {
154164
const result = selectors.selectQueryParams(state);
155165

156-
expect(result).toEqual(
157-
state.router.state.root.firstChild.firstChild.queryParams
158-
);
166+
expect(result).toEqual(state.router.state.root.queryParams);
159167
});
160168

161169
it('should create a selector for selecting a specific query param', () => {
162170
const result = selectors.selectQueryParam('ref')(state);
163171

164-
expect(result).toEqual(
165-
state.router.state.root.firstChild.firstChild.queryParams.ref
166-
);
172+
expect(result).toEqual(state.router.state.root.queryParams.ref);
167173
});
168174

169175
it('should create a selector for selecting the route params', () => {

modules/router-store/src/router_selectors.ts

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,33 @@ import { createSelector } from '@ngrx/store';
22
import { RouterStateSelectors } from './models';
33
import { RouterReducerState } from './reducer';
44

5-
export function getSelectors<V>(
6-
selectState: (state: V) => RouterReducerState<any>
7-
): RouterStateSelectors<V>;
85
export function getSelectors<V>(
96
selectState: (state: V) => RouterReducerState<any>
107
): RouterStateSelectors<V> {
118
const selectRouterState = createSelector(
129
selectState,
1310
(router) => router && router.state
1411
);
15-
const selectCurrentRoute = createSelector(
12+
const selectRootRoute = createSelector(
1613
selectRouterState,
17-
(routerState) => {
18-
if (!routerState) {
19-
return undefined;
20-
}
21-
let route = routerState.root;
22-
while (route.firstChild) {
23-
route = route.firstChild;
24-
}
25-
return route;
26-
}
14+
(routerState) => routerState && routerState.root
2715
);
16+
const selectCurrentRoute = createSelector(selectRootRoute, (rootRoute) => {
17+
if (!rootRoute) {
18+
return undefined;
19+
}
20+
let route = rootRoute;
21+
while (route.firstChild) {
22+
route = route.firstChild;
23+
}
24+
return route;
25+
});
2826
const selectFragment = createSelector(
29-
selectCurrentRoute,
27+
selectRootRoute,
3028
(route) => route && route.fragment
3129
);
3230
const selectQueryParams = createSelector(
33-
selectCurrentRoute,
31+
selectRootRoute,
3432
(route) => route && route.queryParams
3533
);
3634
const selectQueryParam = (param: string) =>

projects/ngrx.io/content/guide/migration/v11.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,63 @@ The new method name `setAll` describes the intention better.
5858
```ts
5959
adapter.setAll(action.entities, state);
6060
```
61+
62+
### @ngrx/router-store
63+
64+
#### Optimized `selectQueryParams`, `selectQueryParam` and `selectFragment` selectors
65+
66+
They select query parameters/fragment from the root router state node instead of the last router state node.
67+
68+
BEFORE:
69+
70+
```ts
71+
const queryParams$ = this.store.select(selectQueryParams);
72+
const fragment$ = this.store.select(selectFragment);
73+
74+
/*
75+
router state:
76+
{
77+
root: {
78+
queryParams: {
79+
search: 'foo',
80+
},
81+
fragment: 'bar',
82+
firstChild: {
83+
queryParams: {
84+
search: 'foo', 👈 query parameters are selected from here
85+
},
86+
fragment: 'bar', 👈 fragment is selected from here
87+
firstChild: undefined,
88+
},
89+
},
90+
url: '/books?search=foo#bar',
91+
}
92+
*/
93+
```
94+
95+
AFTER:
96+
97+
```ts
98+
const queryParams$ = this.store.select(selectQueryParams);
99+
const fragment$ = this.store.select(selectFragment);
100+
101+
/*
102+
router state:
103+
{
104+
root: {
105+
queryParams: {
106+
search: 'foo', 👈 query parameters are selected from here
107+
},
108+
fragment: 'bar', 👈 fragment is selected from here
109+
firstChild: {
110+
queryParams: {
111+
search: 'foo',
112+
},
113+
fragment: 'bar',
114+
firstChild: undefined,
115+
},
116+
},
117+
url: '/books?search=foo#bar',
118+
}
119+
*/
120+
```

0 commit comments

Comments
 (0)