or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

action-creators.mdfeature-management.mdindex.mdmodule-configuration.mdreducer-creators.mdselectors.mdstandalone-providers.mdstore-service.mdtesting-utilities.md

selectors.mddocs/

0

# Selectors

1

2

Selectors provide efficient, memoized state selection with composition capabilities. They enable derived state computation, performance optimization through memoization, and type-safe state access patterns.

3

4

## Capabilities

5

6

### Create Selector

7

8

Creates memoized selectors that efficiently compute derived state with automatic memoization and composition support.

9

10

```typescript { .api }

11

/**

12

* Creates a memoized selector from input selectors and projector function

13

* @param selectors - Array of input selectors

14

* @param projector - Function that computes the final result

15

* @returns MemoizedSelector with memoization and lifecycle methods

16

*/

17

function createSelector<Selectors extends readonly any[], Result>(

18

selectors: [...Selectors],

19

projector: (...args: SelectorResults<Selectors>) => Result

20

): MemoizedSelector<any, Result>;

21

22

interface MemoizedSelector<State, Result, ProjectorFn = DefaultProjectorFn<Result>>

23

extends Selector<State, Result> {

24

/** Release memoized values and reset internal state */

25

release(): void;

26

/** The projector function used to compute the result */

27

projector: ProjectorFn;

28

/** Override the result value (useful for testing) */

29

setResult: (result?: Result) => void;

30

/** Clear any overridden result value */

31

clearResult: () => void;

32

}

33

```

34

35

**Usage Examples:**

36

37

```typescript

38

import { createSelector } from "@ngrx/store";

39

40

interface AppState {

41

users: UserState;

42

orders: OrderState;

43

ui: UiState;

44

}

45

46

// Feature selectors

47

const selectUserState = (state: AppState) => state.users;

48

const selectOrderState = (state: AppState) => state.orders;

49

50

// Basic selector

51

const selectAllUsers = createSelector(

52

selectUserState,

53

(userState) => userState.entities

54

);

55

56

// Composed selector with multiple inputs

57

const selectActiveUserOrders = createSelector(

58

selectAllUsers,

59

selectOrderState,

60

(users, orderState, props: { userId: string }) => {

61

const user = users.find(u => u.id === props.userId);

62

return user?.active ? orderState.entities.filter(o => o.userId === props.userId) : [];

63

}

64

);

65

66

// Complex derived state

67

const selectOrderSummary = createSelector(

68

selectOrderState,

69

selectAllUsers,

70

(orderState, users) => {

71

const orders = orderState.entities;

72

const totalRevenue = orders.reduce((sum, order) => sum + order.total, 0);

73

const ordersByStatus = orders.reduce((acc, order) => {

74

acc[order.status] = (acc[order.status] || 0) + 1;

75

return acc;

76

}, {} as Record<string, number>);

77

78

return {

79

totalOrders: orders.length,

80

totalRevenue,

81

averageOrderValue: totalRevenue / orders.length || 0,

82

ordersByStatus,

83

topCustomers: users

84

.map(user => ({

85

...user,

86

orderCount: orders.filter(o => o.userId === user.id).length

87

}))

88

.sort((a, b) => b.orderCount - a.orderCount)

89

.slice(0, 5)

90

};

91

}

92

);

93

```

94

95

### Create Feature Selector

96

97

Creates selectors for accessing feature state slices in the store.

98

99

```typescript { .api }

100

/**

101

* Creates a selector for a specific feature slice of state

102

* @param featureKey - The key of the feature in the state

103

* @returns MemoizedSelector for the feature state

104

*/

105

function createFeatureSelector<T, K extends keyof T>(

106

featureKey: K

107

): MemoizedSelector<T, T[K]>;

108

```

109

110

**Usage Examples:**

111

112

```typescript

113

import { createFeatureSelector, createSelector } from "@ngrx/store";

114

115

// Create feature selectors

116

const selectUserFeature = createFeatureSelector<AppState, 'users'>('users');

117

const selectOrderFeature = createFeatureSelector<AppState, 'orders'>('orders');

118

const selectUiFeature = createFeatureSelector<AppState, 'ui'>('ui');

119

120

// Use feature selectors as base for other selectors

121

const selectUserList = createSelector(

122

selectUserFeature,

123

(userState) => userState.entities

124

);

125

126

const selectCurrentUser = createSelector(

127

selectUserFeature,

128

(userState) => userState.entities.find(u => u.id === userState.selectedUserId)

129

);

130

131

const selectIsLoading = createSelector(

132

selectUserFeature,

133

selectOrderFeature,

134

(userState, orderState) => userState.loading || orderState.loading

135

);

136

```

137

138

### Create Selector Factory

139

140

Creates custom selector factories with configurable memoization strategies.

141

142

```typescript { .api }

143

/**

144

* Creates a selector factory with custom memoization

145

* @param memoize - Custom memoization function

146

* @returns SelectorFactory for creating selectors with custom memoization

147

*/

148

function createSelectorFactory<T, V>(memoize: MemoizeFn): SelectorFactory<T, V>;

149

150

/**

151

* Function that adds memoization to any function

152

*/

153

type MemoizeFn = (fn: AnyFn) => MemoizedProjection;

154

155

/**

156

* Comparison function for memoization

157

*/

158

type ComparatorFn = (a: any, b: any) => boolean;

159

160

interface MemoizedProjection {

161

memoized: AnyFn;

162

reset: () => void;

163

setResult: (result?: any) => void;

164

clearResult: () => void;

165

}

166

```

167

168

**Usage Examples:**

169

170

```typescript

171

import { createSelectorFactory, defaultMemoize, resultMemoize } from "@ngrx/store";

172

173

// Custom memoization that only checks result equality

174

const createDeepEqualSelector = createSelectorFactory(

175

(projectionFn) => resultMemoize(projectionFn, deepEqual)

176

);

177

178

// Selector with custom memoization

179

const selectComplexData = createDeepEqualSelector(

180

selectUserState,

181

selectOrderState,

182

(userState, orderState) => {

183

// Expensive computation that returns complex object

184

return computeComplexAnalytics(userState, orderState);

185

}

186

);

187

188

// No memoization selector for always-fresh data

189

const createNonMemoizedSelector = createSelectorFactory(

190

(projectionFn) => ({

191

memoized: projectionFn,

192

reset: () => {},

193

setResult: () => {},

194

clearResult: () => {}

195

})

196

);

197

198

const selectCurrentTime = createNonMemoizedSelector(

199

selectUiState,

200

(uiState) => ({

201

...uiState.timeConfig,

202

currentTime: Date.now()

203

})

204

);

205

```

206

207

### Default Memoization Functions

208

209

Built-in memoization strategies for different use cases.

210

211

```typescript { .api }

212

/**

213

* Default memoization function with argument and result equality checking

214

* @param projectionFn - Function to memoize

215

* @param isArgumentsEqual - Function to compare arguments (default: strict equality)

216

* @param isResultEqual - Function to compare results (default: strict equality)

217

* @returns MemoizedProjection with reset and override capabilities

218

*/

219

function defaultMemoize(

220

projectionFn: AnyFn,

221

isArgumentsEqual?: ComparatorFn,

222

isResultEqual?: ComparatorFn

223

): MemoizedProjection;

224

225

/**

226

* Result-based memoization that only checks result equality

227

* @param projectionFn - Function to memoize

228

* @param isResultEqual - Function to compare results

229

* @returns MemoizedProjection focused on result comparison

230

*/

231

function resultMemoize(

232

projectionFn: AnyFn,

233

isResultEqual: ComparatorFn

234

): MemoizedProjection;

235

236

/**

237

* Default state function (identity function)

238

*/

239

function defaultStateFn<T>(state: T): T;

240

241

/**

242

* Default equality check using strict equality

243

*/

244

function isEqualCheck(a: any, b: any): boolean;

245

```

246

247

## Core Type Definitions

248

249

### Selector Types

250

251

```typescript { .api }

252

/**

253

* Function that selects a value from state

254

*/

255

type Selector<T, V> = (state: T) => V;

256

257

/**

258

* @deprecated Selector with additional props parameter

259

*/

260

type SelectorWithProps<State, Props, Result> = (state: State, props: Props) => Result;

261

262

/**

263

* Default projector function type

264

*/

265

type DefaultProjectorFn<T> = (...args: any[]) => T;

266

267

/**

268

* Any function type for memoization

269

*/

270

type AnyFn = (...args: any[]) => any;

271

```

272

273

## Advanced Usage Patterns

274

275

### Performance-Optimized Selectors

276

277

```typescript

278

import { createSelector, createSelectorFactory, defaultMemoize } from "@ngrx/store";

279

280

// Custom memoization for expensive computations

281

const createExpensiveSelector = createSelectorFactory(

282

(projectionFn) => defaultMemoize(

283

projectionFn,

284

// Custom argument equality - only recompute if IDs change

285

(argsA, argsB) => {

286

return argsA[0]?.id === argsB[0]?.id && argsA[1]?.version === argsB[1]?.version;

287

},

288

// Custom result equality - deep comparison

289

(a, b) => JSON.stringify(a) === JSON.stringify(b)

290

)

291

);

292

293

const selectExpensiveCalculation = createExpensiveSelector(

294

selectLargeDataset,

295

selectConfiguration,

296

(dataset, config) => {

297

// Expensive computation

298

return performComplexAnalysis(dataset, config);

299

}

300

);

301

```

302

303

### Parameterized Selectors

304

305

```typescript

306

// Selector factory pattern for parameterized selection

307

const makeSelectUserById = () => createSelector(

308

selectAllUsers,

309

(users, props: { id: string }) => users.find(user => user.id === props.id)

310

);

311

312

const makeSelectUserOrders = () => createSelector(

313

selectAllOrders,

314

(orders, props: { userId: string }) => orders.filter(order => order.userId === props.userId)

315

);

316

317

// Usage in components

318

class UserComponent {

319

@Input() userId!: string;

320

321

private selectUser = makeSelectUserById();

322

private selectOrders = makeSelectUserOrders();

323

324

user$ = this.store.select(this.selectUser, { id: this.userId });

325

orders$ = this.store.select(this.selectOrders, { userId: this.userId });

326

}

327

```

328

329

### Conditional Selectors

330

331

```typescript

332

const selectConditionalData = createSelector(

333

selectUserState,

334

selectAppConfig,

335

(userState, config) => {

336

// Conditional logic in selector

337

if (config.featureFlags.advancedMode) {

338

return {

339

...userState,

340

advancedMetrics: computeAdvancedMetrics(userState.entities),

341

recommendations: generateRecommendations(userState.entities)

342

};

343

}

344

345

return {

346

basicData: userState.entities.map(user => ({

347

id: user.id,

348

name: user.name,

349

status: user.status

350

}))

351

};

352

}

353

);

354

```

355

356

### Selector Composition and Reuse

357

358

```typescript

359

// Base selectors

360

const selectActiveUsers = createSelector(

361

selectAllUsers,

362

users => users.filter(user => user.active)

363

);

364

365

const selectPremiumUsers = createSelector(

366

selectAllUsers,

367

users => users.filter(user => user.subscription === 'premium')

368

);

369

370

// Composed selectors

371

const selectActivePremiumUsers = createSelector(

372

selectActiveUsers,

373

selectPremiumUsers,

374

(activeUsers, premiumUsers) =>

375

activeUsers.filter(user => premiumUsers.some(p => p.id === user.id))

376

);

377

378

const selectUserStatistics = createSelector(

379

selectAllUsers,

380

selectActiveUsers,

381

selectPremiumUsers,

382

(allUsers, activeUsers, premiumUsers) => ({

383

total: allUsers.length,

384

active: activeUsers.length,

385

premium: premiumUsers.length,

386

activeRatio: activeUsers.length / allUsers.length,

387

premiumRatio: premiumUsers.length / allUsers.length

388

})

389

);

390

```

391

392

## Testing Selectors

393

394

```typescript

395

import { selectUserSummary } from './user.selectors';

396

397

describe('User Selectors', () => {

398

it('should select user summary', () => {

399

const mockState = {

400

users: {

401

entities: [

402

{ id: '1', name: 'John', active: true },

403

{ id: '2', name: 'Jane', active: false }

404

]

405

}

406

};

407

408

const result = selectUserSummary(mockState);

409

410

expect(result).toEqual({

411

total: 2,

412

active: 1,

413

inactive: 1

414

});

415

});

416

417

it('should memoize results', () => {

418

const state1 = { users: { entities: [] } };

419

const state2 = { users: { entities: [] } };

420

421

const result1 = selectUserSummary(state1);

422

const result2 = selectUserSummary(state2);

423

424

// Same reference due to memoization

425

expect(result1).toBe(result2);

426

});

427

});

428

```

429

430

## Best Practices

431

432

1. **Use Feature Selectors**: Start with `createFeatureSelector` for clear state structure

433

2. **Compose Selectors**: Build complex selectors from simpler ones for reusability

434

3. **Memoization Strategy**: Choose appropriate memoization based on data characteristics

435

4. **Pure Functions**: Keep selector functions pure and side-effect free

436

5. **Performance**: Use custom memoization for expensive computations

437

6. **Testing**: Test selectors independently with mock state data