0
# Effect Creation and Management
1
2
Core functionality for creating and managing effects in NgRx applications, supporting both class-based and functional patterns with comprehensive configuration options.
3
4
## Capabilities
5
6
### createEffect Function
7
8
Creates effects from source functions with configuration options, supporting both class-based and functional patterns.
9
10
```typescript { .api }
11
/**
12
* Creates an effect from a source function with optional configuration
13
* @param source - Function returning an Observable
14
* @param config - Optional effect configuration
15
* @returns Effect with metadata for registration
16
*/
17
function createEffect<
18
C extends EffectConfig & { functional?: false },
19
DT extends DispatchType<C>,
20
OTP,
21
R extends EffectResult<OT>,
22
OT extends ObservableType<DT, OTP>
23
>(
24
source: () => R & ConditionallyDisallowActionCreator<DT, R>,
25
config?: C
26
): R & CreateEffectMetadata;
27
28
function createEffect<Source extends () => Observable<unknown>>(
29
source: Source,
30
config: EffectConfig & { functional: true; dispatch: false }
31
): FunctionalEffect<Source>;
32
33
function createEffect<Source extends () => Observable<Action>>(
34
source: Source & ConditionallyDisallowActionCreator<true, ReturnType<Source>>,
35
config: EffectConfig & { functional: true; dispatch?: true }
36
): FunctionalEffect<Source>;
37
38
function createEffect<
39
Result extends EffectResult<unknown>,
40
Source extends () => Result
41
>(
42
source: Source,
43
config?: EffectConfig
44
): (Source | Result) & CreateEffectMetadata;
45
46
type FunctionalEffect<
47
Source extends () => Observable<unknown> = () => Observable<unknown>
48
> = Source & FunctionalCreateEffectMetadata;
49
50
interface FunctionalCreateEffectMetadata extends CreateEffectMetadata {
51
'__@ngrx/effects_create__': EffectConfig & { functional: true };
52
}
53
54
interface CreateEffectMetadata {
55
'__@ngrx/effects_create__': EffectConfig;
56
}
57
```
58
59
**Usage Examples:**
60
61
```typescript
62
import { Injectable, inject } from "@angular/core";
63
import { createEffect, Actions, ofType } from "@ngrx/effects";
64
import { map, switchMap, catchError } from "rxjs/operators";
65
import { of } from "rxjs";
66
67
// Class-based effect
68
@Injectable()
69
export class BookEffects {
70
constructor(private actions$: Actions) {}
71
72
loadBooks$ = createEffect(() =>
73
this.actions$.pipe(
74
ofType(BookActions.loadBooks),
75
switchMap(() =>
76
this.bookService.getBooks().pipe(
77
map(books => BookActions.loadBooksSuccess({ books })),
78
catchError(error => of(BookActions.loadBooksFailure({ error })))
79
)
80
)
81
)
82
);
83
84
// Non-dispatching effect
85
logBookActions$ = createEffect(() =>
86
this.actions$.pipe(
87
ofType(BookActions.loadBooksSuccess),
88
map(action => console.log('Books loaded:', action.books))
89
),
90
{ dispatch: false }
91
);
92
}
93
94
// Functional effect
95
export const loadBooksEffect = createEffect(
96
(actions$ = inject(Actions), bookService = inject(BookService)) =>
97
actions$.pipe(
98
ofType(BookActions.loadBooks),
99
switchMap(() =>
100
bookService.getBooks().pipe(
101
map(books => BookActions.loadBooksSuccess({ books })),
102
catchError(error => of(BookActions.loadBooksFailure({ error })))
103
)
104
)
105
),
106
{ functional: true }
107
);
108
109
// Effect with custom error handling disabled
110
export const customErrorHandlingEffect = createEffect(
111
() => actions$.pipe(
112
ofType(SomeActions.customAction),
113
switchMap(() =>
114
someService.riskyOperation().pipe(
115
map(result => SomeActions.success({ result })),
116
// Custom error handling
117
catchError(error => {
118
console.error('Custom error handling:', error);
119
return of(SomeActions.failure({ error }));
120
})
121
)
122
)
123
),
124
{ useEffectsErrorHandler: false }
125
);
126
```
127
128
### Effect Configuration
129
130
Configuration interface for customizing effect behavior.
131
132
```typescript { .api }
133
interface EffectConfig {
134
/** Whether the effect should dispatch actions (default: true) */
135
dispatch?: boolean;
136
/** Whether this is a functional effect (default: false) */
137
functional?: boolean;
138
/** Whether to use the effects error handler (default: true) */
139
useEffectsErrorHandler?: boolean;
140
}
141
```
142
143
**Configuration Options:**
144
145
- **`dispatch`**: When `false`, the effect won't dispatch actions to the store (useful for side effects like logging, navigation, etc.)
146
- **`functional`**: Must be `true` for functional effects created outside of classes
147
- **`useEffectsErrorHandler`**: When `false`, disables automatic error handling, allowing custom error handling strategies
148
149
### Effect Metadata
150
151
Metadata interface attached to created effects for runtime processing.
152
153
```typescript { .api }
154
interface CreateEffectMetadata {
155
/** Internal marker for NgRx effects system */
156
'__@ngrx/effects_create__': EffectConfig;
157
}
158
159
/**
160
* Extract effect metadata from effect instances
161
* @param instance - Effect instance to extract metadata from
162
* @returns Array of effect metadata
163
*/
164
function getEffectsMetadata<T>(instance: T): Array<{
165
propertyName: string;
166
dispatch: boolean;
167
useEffectsErrorHandler: boolean;
168
}>;
169
```
170
171
### Advanced Effect Patterns
172
173
**Conditional Effects:**
174
175
```typescript
176
loadUserData$ = createEffect(() =>
177
this.actions$.pipe(
178
ofType(UserActions.loadUser),
179
switchMap(action =>
180
this.authService.isAuthenticated$.pipe(
181
switchMap(isAuthenticated =>
182
isAuthenticated
183
? this.userService.getUser(action.userId).pipe(
184
map(user => UserActions.loadUserSuccess({ user }))
185
)
186
: of(UserActions.loadUserFailure({ error: 'Not authenticated' }))
187
)
188
)
189
)
190
)
191
);
192
```
193
194
**Debounced Search Effect:**
195
196
```typescript
197
search$ = createEffect(() =>
198
this.actions$.pipe(
199
ofType(SearchActions.search),
200
debounceTime(300),
201
distinctUntilChanged(),
202
switchMap(action =>
203
this.searchService.search(action.query).pipe(
204
map(results => SearchActions.searchSuccess({ results })),
205
catchError(error => of(SearchActions.searchFailure({ error })))
206
)
207
)
208
)
209
);
210
```
211
212
**Non-dispatching Effect for Side Effects:**
213
214
```typescript
215
saveToLocalStorage$ = createEffect(() =>
216
this.actions$.pipe(
217
ofType(PreferencesActions.updatePreferences),
218
tap(action => {
219
localStorage.setItem('preferences', JSON.stringify(action.preferences));
220
})
221
),
222
{ dispatch: false }
223
);
224
```