or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

action-generation.mdcomponent-store.mdcontainer-components.mddata-services.mdeffect-generation.mdentity-management.mdfeature-generation.mdindex.mdngrx-push-migration.mdreducer-generation.mdselector-generation.mdstore-setup.mdutility-functions.md

reducer-generation.mddocs/

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

```