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
```