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

store-service.mddocs/

0

# Store Service

1

2

The Store service is the core reactive state container that provides the primary interface for dispatching actions and selecting state. It extends Observable<T> and implements Observer<Action>, offering both reactive and imperative APIs.

3

4

## Capabilities

5

6

### Store Class

7

8

Main store service that manages application state with reactive capabilities.

9

10

```typescript { .api }

11

/**

12

* Injectable service that provides reactive state management and a public API for dispatching actions

13

*/

14

class Store<T = object> extends Observable<T> implements Observer<Action> {

15

/** Current state as Angular Signal */

16

readonly state: Signal<T>;

17

18

/** Create signal from selector function */

19

selectSignal<K>(

20

selector: (state: T) => K,

21

options?: SelectSignalOptions<K>

22

): Signal<K>;

23

24

/** Dispatch action to update state */

25

dispatch<V extends Action>(action: V & CreatorsNotAllowedCheck<V>): void;

26

27

/** Dispatch function-based action with automatic effect management */

28

dispatch<V extends () => Action>(

29

dispatchFn: V & CreatorsNotAllowedCheck<V>,

30

config?: { injector: Injector }

31

): EffectRef;

32

33

/** Select state slice using function (Observable-based) */

34

select<K>(mapFn: (state: T) => K): Observable<K>;

35

36

/** Add reducer dynamically at runtime */

37

addReducer<State, Actions extends Action = Action>(

38

key: string,

39

reducer: ActionReducer<State, Actions>

40

): void;

41

42

/** Remove reducer dynamically at runtime */

43

removeReducer<Key extends Extract<keyof T, string>>(key: Key): void;

44

45

/** Lift operator for creating derived stores */

46

lift<R>(operator: Operator<T, R>): Store<R>;

47

48

/** Observer interface implementation */

49

next(action: Action): void;

50

error(err: any): void;

51

complete(): void;

52

}

53

54

interface SelectSignalOptions<T> {

55

/** Comparison function for equality checking */

56

equal?: ValueEqualityFn<T>;

57

}

58

```

59

60

**Usage Examples:**

61

62

```typescript

63

import { Component, inject } from "@angular/core";

64

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

65

import { selectUser, selectUserName } from "./user.selectors";

66

import { loadUser, updateUserName } from "./user.actions";

67

68

@Component({

69

selector: 'app-user-profile',

70

template: `

71

<div>

72

<h2>{{ userName() }}</h2>

73

<button (click)="loadUserData()">Load User</button>

74

<button (click)="updateName()">Update Name</button>

75

</div>

76

`

77

})

78

export class UserProfileComponent {

79

private store = inject(Store);

80

81

// Signal-based state selection (recommended)

82

userName = this.store.selectSignal(selectUserName);

83

user = this.store.selectSignal(selectUser);

84

85

// Observable-based state selection

86

user$ = this.store.select(selectUser);

87

88

loadUserData() {

89

this.store.dispatch(loadUser());

90

}

91

92

updateName() {

93

this.store.dispatch(updateUserName({ name: 'New Name' }));

94

}

95

96

// Function-based dispatch with automatic effect management

97

autoSaveUser() {

98

const effectRef = this.store.dispatch(() => {

99

const currentUser = this.user();

100

if (currentUser?.isDirty) {

101

return saveUser({ user: currentUser });

102

}

103

return noOp();

104

});

105

106

// Effect will run automatically when dependencies change

107

}

108

}

109

```

110

111

### Select Operator

112

113

Standalone operator function for use in RxJS pipelines.

114

115

```typescript { .api }

116

/**

117

* Selects state using a mapping function in RxJS pipelines

118

*/

119

function select<T, K>(mapFn: (state: T) => K): (source$: Observable<T>) => Observable<K>;

120

```

121

122

**Usage Examples:**

123

124

```typescript

125

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

126

import { pipe } from "rxjs";

127

import { map, distinctUntilChanged } from "rxjs/operators";

128

129

// Use in custom observables

130

const userAge$ = state$.pipe(

131

select(state => state.user.age),

132

map(age => age >= 18 ? 'adult' : 'minor')

133

);

134

135

// Compose with other operators

136

const filteredUsers$ = state$.pipe(

137

select(state => state.users),

138

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

139

distinctUntilChanged()

140

);

141

```

142

143

### Store Providers

144

145

Provider configuration for dependency injection.

146

147

```typescript { .api }

148

/**

149

* Provider array for Store service injection

150

*/

151

const STORE_PROVIDERS: Provider[];

152

```

153

154

## Dynamic Reducer Management

155

156

The Store service supports dynamic addition and removal of reducers at runtime, enabling lazy loading and feature modules.

157

158

```typescript

159

// Add reducer dynamically

160

store.addReducer('newFeature', newFeatureReducer);

161

162

// Remove reducer when feature is unloaded

163

store.removeReducer('oldFeature');

164

```

165

166

## Signal Integration

167

168

The Store service provides modern Angular Signals support through `selectSignal()`:

169

170

```typescript

171

// Create computed signals from state

172

const userName = store.selectSignal(state => state.user.name);

173

const isLoggedIn = store.selectSignal(state => !!state.user.id);

174

175

// Use in templates

176

template: `<div>Welcome {{ userName() }}!</div>`

177

178

// Custom equality checking

179

const complexData = store.selectSignal(

180

state => state.complexObject,

181

{ equal: (a, b) => a.id === b.id && a.version === b.version }

182

);

183

```

184

185

## Function-based Dispatch

186

187

Modern reactive dispatch pattern that automatically manages effects:

188

189

```typescript

190

// Traditional dispatch

191

store.dispatch(someAction());

192

193

// Function-based dispatch with automatic effect management

194

const effectRef = store.dispatch(() => {

195

// This function runs reactively when dependencies change

196

const currentState = store.state();

197

return currentState.shouldUpdate ? updateAction() : noOpAction();

198

});

199

200

// Effect automatically manages its lifecycle

201

// Call effectRef.destroy() to clean up if needed

202

```

203

204

## Core Services

205

206

### Actions Subject

207

208

Injectable service that extends BehaviorSubject to manage action dispatching with validation.

209

210

```typescript { .api }

211

/**

212

* Injectable service that dispatches actions and validates them before emission

213

*/

214

class ActionsSubject extends BehaviorSubject<Action> implements OnDestroy {

215

/** Dispatches action with validation */

216

next(action: Action): void;

217

/** Completes the subject (no-op for actions) */

218

complete(): void;

219

/** Angular lifecycle hook for cleanup */

220

ngOnDestroy(): void;

221

}

222

223

/** Initial action type dispatched when store initializes */

224

const INIT: '@ngrx/store/init';

225

226

/** Provider configuration for ActionsSubject */

227

const ACTIONS_SUBJECT_PROVIDERS: Provider[];

228

```

229

230

**Usage Examples:**

231

232

```typescript

233

import { inject } from "@angular/core";

234

import { ActionsSubject, INIT } from "@ngrx/store";

235

import { filter } from "rxjs/operators";

236

237

@Injectable()

238

export class ActionLogger {

239

private actions$ = inject(ActionsSubject);

240

241

constructor() {

242

// Listen to all actions

243

this.actions$.pipe(

244

filter(action => action.type !== INIT)

245

).subscribe(action => {

246

console.log('Action dispatched:', action.type, action);

247

});

248

}

249

}

250

251

// Custom action monitoring

252

const customActionMonitor = inject(ActionsSubject).pipe(

253

filter(action => action.type.startsWith('[User]'))

254

).subscribe(userAction => {

255

// Handle user-specific actions

256

handleUserAction(userAction);

257

});

258

```

259

260

### Reducer Manager

261

262

Service that manages dynamic reducer registration and composition.

263

264

```typescript { .api }

265

/**

266

* Injectable service for dynamic reducer management

267

*/

268

class ReducerManager extends BehaviorSubject<ActionReducer<any, any>> implements OnDestroy {

269

/** Current reducer map */

270

get currentReducers(): ActionReducerMap<any, any>;

271

272

/** Add feature with reducers and configuration */

273

addFeature(feature: StoreFeature<any, any>): void;

274

addFeatures(features: StoreFeature<any, any>[]): void;

275

276

/** Remove feature by reference */

277

removeFeature(feature: StoreFeature<any, any>): void;

278

removeFeatures(features: StoreFeature<any, any>[]): void;

279

280

/** Add single reducer */

281

addReducer(key: string, reducer: ActionReducer<any, any>): void;

282

addReducers(reducers: ActionReducerMap<any, any>): void;

283

284

/** Remove reducer by key */

285

removeReducer(key: string): void;

286

removeReducers(featureKeys: string[]): void;

287

288

/** Angular lifecycle hook */

289

ngOnDestroy(): void;

290

}

291

292

/** Abstract base class for reducer observables */

293

abstract class ReducerObservable extends Observable<ActionReducer<any, any>> {}

294

295

/** Abstract dispatcher for reducer manager actions */

296

abstract class ReducerManagerDispatcher extends ActionsSubject {}

297

298

/** Action type for reducer updates */

299

const UPDATE: '@ngrx/store/update-reducers';

300

```

301

302

**Usage Examples:**

303

304

```typescript

305

import { inject } from "@angular/core";

306

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

307

308

@Injectable()

309

export class DynamicFeatureService {

310

private reducerManager = inject(ReducerManager);

311

312

loadFeature(featureName: string) {

313

// Dynamically add feature reducer

314

this.reducerManager.addReducer(featureName, featureReducer);

315

316

// Add complete feature configuration

317

this.reducerManager.addFeature({

318

key: featureName,

319

reducers: featureReducerMap,

320

reducerFactory: combineReducers,

321

initialState: featureInitialState,

322

metaReducers: [loggingMetaReducer]

323

});

324

}

325

326

unloadFeature(featureName: string) {

327

// Remove reducer when feature is no longer needed

328

this.reducerManager.removeReducer(featureName);

329

}

330

331

// Monitor reducer changes

332

trackReducerChanges() {

333

this.reducerManager.subscribe(currentReducer => {

334

console.log('Root reducer updated');

335

});

336

}

337

}

338

```

339

340

### State Service

341

342

Core state management service with Signal integration.

343

344

```typescript { .api }

345

/**

346

* Core state service that manages the application state tree

347

*/

348

class State<T> extends BehaviorSubject<T> implements OnDestroy {

349

/** Current state as Angular Signal */

350

readonly state: Signal<T>;

351

352

/** Static reference to INIT action */

353

static readonly INIT: '@ngrx/store/init';

354

355

/** Angular lifecycle hook */

356

ngOnDestroy(): void;

357

}

358

359

/** Abstract base class for state observables */

360

abstract class StateObservable extends Observable<any> {

361

/** Current state as Angular Signal */

362

abstract readonly state: Signal<any>;

363

}

364

365

/** Reduces state with given action using current reducer */

366

function reduceState<T, V extends Action = Action>(

367

state: T | undefined,

368

action: V

369

): T;

370

```

371

372

**Usage Examples:**

373

374

```typescript

375

import { inject, Signal } from "@angular/core";

376

import { State, StateObservable } from "@ngrx/store";

377

378

@Injectable()

379

export class StateMonitor {

380

private state = inject(State);

381

382

// Access state as Signal

383

getCurrentState(): Signal<any> {

384

return this.state.state;

385

}

386

387

// Subscribe to state changes

388

watchStateChanges() {

389

this.state.subscribe(currentState => {

390

console.log('State updated:', currentState);

391

});

392

}

393

394

// Get specific state slice

395

getUserState() {

396

return this.state.pipe(

397

map(state => state.user),

398

distinctUntilChanged()

399

);

400

}

401

}

402

403

// Custom state observable

404

@Injectable()

405

export class CustomStateObservable extends StateObservable {

406

readonly state = inject(State).state;

407

408

constructor() {

409

super(subscriber => {

410

// Custom state emission logic

411

this.state().subscribe(subscriber);

412

});

413

}

414

}

415

```

416

417

### Scanned Actions Subject

418

419

Service that tracks processed actions for debugging and development tools.

420

421

```typescript { .api }

422

/**

423

* Subject that emits actions after they have been processed by reducers

424

*/

425

class ScannedActionsSubject extends Subject<Action> {

426

/** Emits processed action */

427

next(action: Action): void;

428

}

429

```

430

431

**Usage Examples:**

432

433

```typescript

434

import { inject } from "@angular/core";

435

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

436

437

@Injectable()

438

export class DevToolsService {

439

private scannedActions$ = inject(ScannedActionsSubject);

440

441

constructor() {

442

// Track processed actions for debugging

443

this.scannedActions$.subscribe(processedAction => {

444

console.log('Action processed:', processedAction.type);

445

this.sendToDevTools(processedAction);

446

});

447

}

448

449

private sendToDevTools(action: Action) {

450

// Send to Redux DevTools or custom debugging tools

451

if (window.__REDUX_DEVTOOLS_EXTENSION__) {

452

window.__REDUX_DEVTOOLS_EXTENSION__.send(action);

453

}

454

}

455

}

456

```