Side effect model for @ngrx/store that manages asynchronous operations and external interactions in Angular applications using RxJS
npx @tessl/cli install tessl/npm-ngrx--effects@20.0.0NgRx Effects is a side effect model for @ngrx/store that provides RxJS-powered effect management for Angular applications. It allows for handling asynchronous operations and external interactions while maintaining separation of concerns between pure state logic and side effects.
npm install @ngrx/effectsimport {
createEffect,
Actions,
ofType,
EffectsModule,
provideEffects,
mergeEffects,
getEffectsMetadata
} from "@ngrx/effects";import { Injectable } from "@angular/core";
import { Actions, createEffect, ofType } from "@ngrx/effects";
import { map, switchMap } from "rxjs/operators";
import { of } from "rxjs";
@Injectable()
export class UserEffects {
constructor(private actions$: Actions) {}
// Class-based effect
loadUsers$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.loadUsers),
switchMap(() =>
this.userService.getUsers().pipe(
map(users => UserActions.loadUsersSuccess({ users })),
catchError(error => of(UserActions.loadUsersFailure({ error })))
)
)
)
);
}
// Functional effect
export const loadUsersEffect = createEffect(
(actions$ = inject(Actions)) =>
actions$.pipe(
ofType(UserActions.loadUsers),
switchMap(() =>
inject(UserService).getUsers().pipe(
map(users => UserActions.loadUsersSuccess({ users }))
)
)
),
{ functional: true }
);NgRx Effects is built around several key components:
createEffect() function for defining effects with metadataActions service providing access to all dispatched actionsofType() operator for type-safe action filteringEffectsModule for module-based setup, provideEffects() for standalone appsCore functionality for creating and managing effects, including both class-based and functional patterns.
function createEffect<
C extends EffectConfig & { functional?: false },
DT extends DispatchType<C>,
OTP,
R extends EffectResult<OT>,
OT extends ObservableType<DT, OTP>
>(
source: () => R & ConditionallyDisallowActionCreator<DT, R>,
config?: C
): R & CreateEffectMetadata;
function createEffect<Source extends () => Observable<unknown>>(
source: Source,
config: EffectConfig & { functional: true; dispatch: false }
): FunctionalEffect<Source>;
function createEffect<Source extends () => Observable<Action>>(
source: Source & ConditionallyDisallowActionCreator<true, ReturnType<Source>>,
config: EffectConfig & { functional: true; dispatch?: true }
): FunctionalEffect<Source>;
function createEffect<
Result extends EffectResult<unknown>,
Source extends () => Result
>(
source: Source,
config?: EffectConfig
): (Source | Result) & CreateEffectMetadata;
interface EffectConfig {
dispatch?: boolean;
functional?: boolean;
useEffectsErrorHandler?: boolean;
}
type DispatchType<T> = T extends { dispatch: infer U } ? U : true;
type ObservableType<T, OriginalType> = T extends false ? OriginalType : Action;
type EffectResult<OT> = Observable<OT> | ((...args: any[]) => Observable<OT>);
type ConditionallyDisallowActionCreator<DT, Result> = DT extends false
? unknown
: Result extends EffectResult<infer OT>
? OT extends ActionCreator
? 'ActionCreator cannot be dispatched. Did you forget to call the action creator function?'
: unknown
: unknown;
type FunctionalEffect<
Source extends () => Observable<unknown> = () => Observable<unknown>
> = Source & FunctionalCreateEffectMetadata;
interface FunctionalCreateEffectMetadata extends CreateEffectMetadata {
'__@ngrx/effects_create__': EffectConfig & { functional: true };
}
interface CreateEffectMetadata {
'__@ngrx/effects_create__': EffectConfig;
}Effect Creation and Management
Action stream management and type-safe action filtering capabilities.
class Actions<V = Action> extends Observable<V> {
lift<R>(operator?: Operator<V, R>): Observable<R>;
}
function ofType<E extends Extract<A, { type: T }>, A extends Action = Action, T extends string = A['type']>(
...allowedTypes: [T, ...T[]]
): OperatorFunction<A, E>;
function ofType<AC extends ActionCreator<string, Creator>, U extends Action = Action>(
...allowedTypes: [AC, ...AC[]]
): OperatorFunction<U, ReturnType<AC>>;Integration with Angular's module system and standalone applications.
class EffectsModule {
static forRoot(effects: Type<any>[]): ModuleWithProviders<EffectsRootModule>;
static forFeature(effects: Type<any>[]): ModuleWithProviders<EffectsFeatureModule>;
}
function provideEffects(
effects: Array<Type<unknown> | Record<string, FunctionalEffect>>
): EnvironmentProviders;
function provideEffects(
...effects: Array<Type<unknown> | Record<string, FunctionalEffect>>
): EnvironmentProviders;
function mergeEffects(...effects: Array<Observable<Action> | (() => Observable<Action>)>): () => Observable<Action>;
function getEffectsMetadata<T extends Record<keyof T, Object>>(instance: T): EffectMetadata<T>[];Lifecycle hooks, error handling, and advanced effect management capabilities.
interface OnIdentifyEffects {
ngrxOnIdentifyEffects(): string;
}
interface OnRunEffects {
ngrxOnRunEffects(resolvedEffects$: Observable<EffectNotification>): Observable<EffectNotification>;
}
interface OnInitEffects {
ngrxOnInitEffects(): Action;
}
type EffectsErrorHandler = <T extends Action>(
observable$: Observable<T>,
errorHandler: ErrorHandler
) => Observable<T>;
function defaultEffectsErrorHandler<T extends Action>(
observable$: Observable<T>,
errorHandler: ErrorHandler,
retryAttemptLeft: number = 10
): Observable<T>;Testing support for effects with mock providers and utilities.
function provideMockActions(source: Observable<any>): FactoryProvider;
function provideMockActions(factory: () => Observable<any>): FactoryProvider;Import testing utilities:
import { provideMockActions } from "@ngrx/effects/testing";interface Action {
type: string;
}
interface ActionCreator<T extends string = string, C extends Creator = Creator> {
readonly type: T;
(...args: any[]): any;
}
interface Creator {
(...args: any[]): object;
}
interface EffectNotification {
effect: Observable<any> | (() => Observable<any>);
propertyName: PropertyKey;
sourceName: string | null;
sourceInstance: any;
notification: ObservableNotification<Action | null | undefined>;
}
interface EffectSources {
addEffects(effectSourceInstance: any): void;
toActions(): Observable<Action>;
}
class EffectsRunner implements OnDestroy {
get isStarted(): boolean;
start(): void;
ngOnDestroy(): void;
}
interface OnDestroy {
ngOnDestroy(): void;
}
interface ObservableNotification<T> {
kind: 'N' | 'E' | 'C';
value?: T;
error?: any;
}
type EffectPropertyKey<T extends Record<keyof T, Object>> = Exclude<
keyof T,
keyof Object
>;
interface EffectMetadata<T extends Record<keyof T, Object>>
extends Required<EffectConfig> {
propertyName: EffectPropertyKey<T>;
}
type EffectsMetadata<T extends Record<keyof T, Object>> = {
[Key in EffectPropertyKey<T>]?: EffectConfig;
};
interface Type<T = any> {
new (...args: any[]): T;
}
interface ModuleWithProviders<T> {
ngModule: Type<T>;
providers?: any[];
}
interface EnvironmentProviders {
ɵproviders: any[];
}
interface FactoryProvider {
provide: any;
useFactory: (...args: any[]) => any;
deps?: any[];
}
interface InjectionToken<T> {
toString(): string;
}
interface Observable<T> {
pipe(...operations: any[]): Observable<any>;
subscribe(...args: any[]): any;
}
interface OperatorFunction<T, R> {
(source: Observable<T>): Observable<R>;
}
interface Operator<T, R> {
call(subscriber: any, source: Observable<T>): any;
}const ROOT_EFFECTS_INIT: "@ngrx/effects/init";
function rootEffectsInit(): Action;
const EFFECTS_ERROR_HANDLER: InjectionToken<EffectsErrorHandler>;
const USER_PROVIDED_EFFECTS: InjectionToken<Array<Type<unknown> | InjectionToken<unknown>>[]>;
const _ROOT_EFFECTS_GUARD: InjectionToken<void>;
const _ROOT_EFFECTS: InjectionToken<[Array<Type<unknown> | Record<string, FunctionalEffect>>]>;
const _ROOT_EFFECTS_INSTANCES: InjectionToken<unknown[]>;
const _FEATURE_EFFECTS: InjectionToken<Array<Type<unknown> | Record<string, FunctionalEffect>>[]>;
const _FEATURE_EFFECTS_INSTANCE_GROUPS: InjectionToken<unknown[][]>;