0
# Reducer Generation
1
2
NgRx reducer generation schematic that creates type-safe reducer functions using createReducer and on() functions to handle state updates in response to dispatched actions. Reducers are pure functions that specify how the application's state changes in response to actions.
3
4
## Capabilities
5
6
### Reducer Schematic
7
8
Generates NgRx reducer functions with proper state management and action handling.
9
10
```bash
11
# Basic reducer generation
12
ng generate @ngrx/schematics:reducer User
13
14
# Reducer with creator functions
15
ng generate @ngrx/schematics:reducer User --creators
16
17
# Feature reducer setup
18
ng generate @ngrx/schematics:reducer User --feature
19
```
20
21
```typescript { .api }
22
/**
23
* Reducer schematic configuration interface
24
*/
25
interface ReducerSchema {
26
/** Name of the reducer (typically entity or feature name) */
27
name: string;
28
/** Path where reducer files should be generated */
29
path?: string;
30
/** Angular project to target */
31
project?: string;
32
/** Generate files without creating a folder */
33
flat?: boolean;
34
/** Group reducer files within folders */
35
group?: boolean;
36
/** Path to existing reducers file for feature setup */
37
reducers?: string;
38
/** Generate as feature reducer */
39
feature?: boolean;
40
/** Use creator functions (createReducer) */
41
creators?: boolean;
42
}
43
```
44
45
### Basic Reducer Generation
46
47
Creates reducer functions using NgRx's createReducer and on() functions with proper state typing.
48
49
```typescript
50
// Generated reducer with state interface
51
import { createReducer, on } from '@ngrx/store';
52
import * as UserActions from './user.actions';
53
54
export interface UserState {
55
users: User[];
56
loading: boolean;
57
error: string | null;
58
selectedUserId: string | null;
59
}
60
61
export const initialState: UserState = {
62
users: [],
63
loading: false,
64
error: null,
65
selectedUserId: null
66
};
67
68
export const userReducer = createReducer(
69
initialState,
70
71
on(UserActions.loadUsers, (state) => ({
72
...state,
73
loading: true,
74
error: null
75
})),
76
77
on(UserActions.loadUsersSuccess, (state, { users }) => ({
78
...state,
79
users,
80
loading: false,
81
error: null
82
})),
83
84
on(UserActions.loadUsersFailure, (state, { error }) => ({
85
...state,
86
loading: false,
87
error
88
})),
89
90
on(UserActions.selectUser, (state, { userId }) => ({
91
...state,
92
selectedUserId: userId
93
})),
94
95
on(UserActions.clearUsers, (state) => ({
96
...state,
97
users: [],
98
selectedUserId: null
99
}))
100
);
101
```
102
103
**Usage Examples:**
104
105
```bash
106
# Generate user reducer
107
ng generate @ngrx/schematics:reducer User --creators
108
109
# Generate product reducer with custom path
110
ng generate @ngrx/schematics:reducer Product --path=src/app/catalog --creators
111
```
112
113
### State Interface Definition
114
115
The schematic generates comprehensive state interfaces with proper typing:
116
117
```typescript { .api }
118
/**
119
* Generated state interface with common patterns
120
*/
121
interface GeneratedState {
122
/** Collection of entities */
123
entities: Entity[];
124
/** Loading state indicator */
125
loading: boolean;
126
/** Error message if any */
127
error: string | null;
128
/** Currently selected entity ID */
129
selectedId: string | null;
130
/** Additional filters or search criteria */
131
filters?: FilterCriteria;
132
/** Pagination information */
133
pagination?: PaginationState;
134
}
135
136
interface PaginationState {
137
page: number;
138
pageSize: number;
139
total: number;
140
}
141
142
interface FilterCriteria {
143
searchTerm?: string;
144
status?: string;
145
dateRange?: DateRange;
146
}
147
```
148
149
### Feature Reducer Integration
150
151
When using `--feature` flag, the schematic integrates with existing reducer files:
152
153
```typescript
154
// Generated feature reducer integration
155
import { ActionReducerMap, MetaReducer } from '@ngrx/store';
156
import * as fromUser from './user.reducer';
157
import * as fromProduct from './product.reducer';
158
159
export interface AppState {
160
user: fromUser.UserState;
161
product: fromProduct.ProductState;
162
}
163
164
export const reducers: ActionReducerMap<AppState> = {
165
user: fromUser.userReducer,
166
product: fromProduct.productReducer
167
};
168
169
export const metaReducers: MetaReducer<AppState>[] = [];
170
```
171
172
**Usage Examples:**
173
174
```bash
175
# Add to existing feature reducers
176
ng generate @ngrx/schematics:reducer Order --feature --reducers=src/app/state/index.ts
177
178
# Create feature reducer with grouping
179
ng generate @ngrx/schematics:reducer Inventory --feature --group
180
```
181
182
### Action Handler Patterns
183
184
The reducer includes common action handling patterns:
185
186
```typescript
187
// CRUD operation handlers
188
export const entityReducer = createReducer(
189
initialState,
190
191
// Load operations
192
on(EntityActions.loadEntities, (state) => ({
193
...state,
194
loading: true,
195
error: null
196
})),
197
198
on(EntityActions.loadEntitiesSuccess, (state, { entities }) => ({
199
...state,
200
entities,
201
loading: false
202
})),
203
204
on(EntityActions.loadEntitiesFailure, (state, { error }) => ({
205
...state,
206
loading: false,
207
error
208
})),
209
210
// Create operations
211
on(EntityActions.createEntitySuccess, (state, { entity }) => ({
212
...state,
213
entities: [...state.entities, entity]
214
})),
215
216
// Update operations
217
on(EntityActions.updateEntitySuccess, (state, { entity }) => ({
218
...state,
219
entities: state.entities.map(e =>
220
e.id === entity.id ? entity : e
221
)
222
})),
223
224
// Delete operations
225
on(EntityActions.deleteEntitySuccess, (state, { id }) => ({
226
...state,
227
entities: state.entities.filter(e => e.id !== id)
228
})),
229
230
// Selection operations
231
on(EntityActions.selectEntity, (state, { id }) => ({
232
...state,
233
selectedId: id
234
})),
235
236
on(EntityActions.clearSelection, (state) => ({
237
...state,
238
selectedId: null
239
}))
240
);
241
```
242
243
### Immutable State Updates
244
245
The generated reducer ensures immutable state updates following NgRx best practices:
246
247
```typescript { .api }
248
/**
249
* Immutable state update patterns used in generated reducers
250
*/
251
interface StateUpdatePatterns {
252
/** Replace array items */
253
replaceInArray: <T>(array: T[], item: T, matcher: (item: T) => boolean) => T[];
254
/** Add to array */
255
addToArray: <T>(array: T[], item: T) => T[];
256
/** Remove from array */
257
removeFromArray: <T>(array: T[], matcher: (item: T) => boolean) => T[];
258
/** Update nested object */
259
updateNested: <T>(state: T, path: string[], value: any) => T;
260
}
261
```
262
263
### Error Handling
264
265
Comprehensive error handling patterns are included:
266
267
```typescript
268
// Error handling in reducers
269
on(EntityActions.loadEntitiesFailure, (state, { error }) => ({
270
...state,
271
loading: false,
272
error: typeof error === 'string' ? error : error.message || 'Unknown error'
273
})),
274
275
on(EntityActions.createEntityFailure, (state, { error }) => ({
276
...state,
277
loading: false,
278
error: `Failed to create entity: ${error}`
279
})),
280
281
// Clear errors on new operations
282
on(EntityActions.loadEntities, EntityActions.createEntity, (state) => ({
283
...state,
284
error: null
285
}))
286
```
287
288
### Reducer Testing
289
290
The generated reducer is fully testable with proper isolation:
291
292
```typescript
293
// Generated reducer tests
294
describe('UserReducer', () => {
295
describe('unknown action', () => {
296
it('should return the previous state', () => {
297
const action = {} as any;
298
const result = userReducer(initialState, action);
299
expect(result).toBe(initialState);
300
});
301
});
302
303
describe('loadUsers action', () => {
304
it('should set loading to true', () => {
305
const action = UserActions.loadUsers();
306
const result = userReducer(initialState, action);
307
expect(result.loading).toBe(true);
308
expect(result.error).toBe(null);
309
});
310
});
311
312
describe('loadUsersSuccess action', () => {
313
it('should update users and set loading to false', () => {
314
const users = [{ id: '1', name: 'John' }];
315
const action = UserActions.loadUsersSuccess({ users });
316
const result = userReducer(initialState, action);
317
expect(result.users).toEqual(users);
318
expect(result.loading).toBe(false);
319
});
320
});
321
});
322
```
323
324
### TypeScript Integration
325
326
Full TypeScript support with strict typing:
327
328
```typescript { .api }
329
/**
330
* Type-safe reducer function signature
331
*/
332
function createTypedReducer<T>(
333
initialState: T,
334
...ons: On<T>[]
335
): ActionReducer<T>;
336
337
/**
338
* Action reducer map for feature states
339
*/
340
interface ActionReducerMap<T> {
341
[key: string]: ActionReducer<T[keyof T]>;
342
}
343
```
344
345
### Performance Optimizations
346
347
The generated reducer includes performance considerations:
348
349
```typescript
350
// Optimized state updates
351
on(EntityActions.updateEntitySuccess, (state, { entity }) => {
352
const index = state.entities.findIndex(e => e.id === entity.id);
353
if (index === -1) return state;
354
355
const entities = [...state.entities];
356
entities[index] = entity;
357
358
return {
359
...state,
360
entities
361
};
362
});
363
364
// Conditional updates to prevent unnecessary renders
365
on(EntityActions.selectEntity, (state, { id }) =>
366
state.selectedId === id ? state : { ...state, selectedId: id }
367
);
368
```