CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-ngrx--effects

Side effect model for @ngrx/store that manages asynchronous operations and external interactions in Angular applications using RxJS

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

effect-creation.mddocs/

Effect Creation and Management

Core functionality for creating and managing effects in NgRx applications, supporting both class-based and functional patterns with comprehensive configuration options.

Capabilities

createEffect Function

Creates effects from source functions with configuration options, supporting both class-based and functional patterns.

/**
 * Creates an effect from a source function with optional configuration
 * @param source - Function returning an Observable
 * @param config - Optional effect configuration
 * @returns Effect with metadata for registration
 */
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;

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;
}

Usage Examples:

import { Injectable, inject } from "@angular/core";
import { createEffect, Actions, ofType } from "@ngrx/effects";
import { map, switchMap, catchError } from "rxjs/operators";
import { of } from "rxjs";

// Class-based effect
@Injectable()
export class BookEffects {
  constructor(private actions$: Actions) {}

  loadBooks$ = createEffect(() =>
    this.actions$.pipe(
      ofType(BookActions.loadBooks),
      switchMap(() =>
        this.bookService.getBooks().pipe(
          map(books => BookActions.loadBooksSuccess({ books })),
          catchError(error => of(BookActions.loadBooksFailure({ error })))
        )
      )
    )
  );

  // Non-dispatching effect
  logBookActions$ = createEffect(() =>
    this.actions$.pipe(
      ofType(BookActions.loadBooksSuccess),
      map(action => console.log('Books loaded:', action.books))
    ),
    { dispatch: false }
  );
}

// Functional effect
export const loadBooksEffect = createEffect(
  (actions$ = inject(Actions), bookService = inject(BookService)) =>
    actions$.pipe(
      ofType(BookActions.loadBooks),
      switchMap(() =>
        bookService.getBooks().pipe(
          map(books => BookActions.loadBooksSuccess({ books })),
          catchError(error => of(BookActions.loadBooksFailure({ error })))
        )
      )
    ),
  { functional: true }
);

// Effect with custom error handling disabled
export const customErrorHandlingEffect = createEffect(
  () => actions$.pipe(
    ofType(SomeActions.customAction),
    switchMap(() => 
      someService.riskyOperation().pipe(
        map(result => SomeActions.success({ result })),
        // Custom error handling
        catchError(error => {
          console.error('Custom error handling:', error);
          return of(SomeActions.failure({ error }));
        })
      )
    )
  ),
  { useEffectsErrorHandler: false }
);

Effect Configuration

Configuration interface for customizing effect behavior.

interface EffectConfig {
  /** Whether the effect should dispatch actions (default: true) */
  dispatch?: boolean;
  /** Whether this is a functional effect (default: false) */
  functional?: boolean;
  /** Whether to use the effects error handler (default: true) */
  useEffectsErrorHandler?: boolean;
}

Configuration Options:

  • dispatch: When false, the effect won't dispatch actions to the store (useful for side effects like logging, navigation, etc.)
  • functional: Must be true for functional effects created outside of classes
  • useEffectsErrorHandler: When false, disables automatic error handling, allowing custom error handling strategies

Effect Metadata

Metadata interface attached to created effects for runtime processing.

interface CreateEffectMetadata {
  /** Internal marker for NgRx effects system */
  '__@ngrx/effects_create__': EffectConfig;
}

/**
 * Extract effect metadata from effect instances
 * @param instance - Effect instance to extract metadata from
 * @returns Array of effect metadata
 */
function getEffectsMetadata<T>(instance: T): Array<{
  propertyName: string;
  dispatch: boolean;
  useEffectsErrorHandler: boolean;
}>;

Advanced Effect Patterns

Conditional Effects:

loadUserData$ = createEffect(() =>
  this.actions$.pipe(
    ofType(UserActions.loadUser),
    switchMap(action =>
      this.authService.isAuthenticated$.pipe(
        switchMap(isAuthenticated =>
          isAuthenticated
            ? this.userService.getUser(action.userId).pipe(
                map(user => UserActions.loadUserSuccess({ user }))
              )
            : of(UserActions.loadUserFailure({ error: 'Not authenticated' }))
        )
      )
    )
  )
);

Debounced Search Effect:

search$ = createEffect(() =>
  this.actions$.pipe(
    ofType(SearchActions.search),
    debounceTime(300),
    distinctUntilChanged(),
    switchMap(action =>
      this.searchService.search(action.query).pipe(
        map(results => SearchActions.searchSuccess({ results })),
        catchError(error => of(SearchActions.searchFailure({ error })))
      )
    )
  )
);

Non-dispatching Effect for Side Effects:

saveToLocalStorage$ = createEffect(() =>
  this.actions$.pipe(
    ofType(PreferencesActions.updatePreferences),
    tap(action => {
      localStorage.setItem('preferences', JSON.stringify(action.preferences));
    })
  ),
  { dispatch: false }
);

docs

actions-filtering.md

advanced-features.md

effect-creation.md

index.md

module-setup.md

testing.md

tile.json