Skip to main content

rxEffects

rxEffects is the functional creation function for RxEffects, a small helper that runs Observable- or Promise-based side effects and cleans up their subscriptions automatically when the current injection context is destroyed. It replaces manual subscribe / takeUntil(destroy$) / ngOnDestroy boilerplate.

Every signature below is source-derived (package 21.1.1, entry point @rx-angular/state/effects).

Why side effects need their own tool: see the RxState reactive discipline & effects concept.

Import

import { rxEffects } from '@rx-angular/state/effects';

Must be called within an injection context, unless an injector is supplied via RxEffectsOptions.

Signature

rxEffects has four overloads: no-arg, setup function, options, and setup function + options:

export function rxEffects(): RxEffects;
export function rxEffects(setupFn: RxEffectsSetupFn): RxEffects;
export function rxEffects(options: RxEffectsOptions): RxEffects;
export function rxEffects(setupFn: RxEffectsSetupFn, options: RxEffectsOptions): RxEffects;
ParamTypeMeaning
setupFnRxEffectsSetupFnOptional. Runs once on creation with a Pick of the handle (register, onDestroy) so effects can be wired inline in one place.
optionsRxEffectsOptionsOptional. Provide an explicit injector so rxEffects() can be called outside an injection context.
returnsRxEffectsThe effects handle (see below).

Returned handle: RxEffects

interface RxEffects {
register<T>(observable: SideEffectObservable<T>, sideEffectOrObserver?: SideEffectFnOrObserver<T>): () => void;
onDestroy: (fn: () => void) => () => void;
}
MemberKindMeaning
registermethodSubscribe to a source and run a side effect on each emission. Returns an unregister callback; call it to cancel that one effect early.
onDestroymethodRegister custom cleanup logic to run when the host context is destroyed. Returns a callback that removes the registration.

The functional handle exposes only register and onDestroy. There is no unregister, registerOnDestroy, or untilEffect; those belonged to the deprecated class API below. To cancel one effect, call the function returned by register.

register

register<T>(
observable: SideEffectObservable<T>,
sideEffectOrObserver?: SideEffectFnOrObserver<T>,
): () => void;
ParamTypeMeaning
observableSideEffectObservable<T> (= ObservableInput<T>)The trigger: any ObservableInput, meaning an Observable, a Promise, an iterable, or a scheduler action.
sideEffectOrObserverSideEffectFnOrObserver<T> (= ((value: T) => void) \| PartialObserver<T>)Optional. The side-effect logic, as either a next callback or a full observer object ({ next, error, complete }). Omit it when the source already performs its effect via tap.
returns() => voidUnregister callback; invoke it to unsubscribe this effect before destruction.

These four forms are equivalent:

register(obs$, doSideEffect);
register(obs$.pipe(tap(doSideEffect)));
register(obs$, { next: doSideEffect }); // can also tap error / complete
register(obs$, {
next: doSideEffect,
error: (e) => handle(e),
});

Error handling. If a registered observer omits error, or its error runs, the error is forwarded to Angular's ErrorHandler (looked up optionally from DI). An error in one effect does not tear down the others, but that individual stream completes, so recover with RxJS retry() / catchError() inside the pipe if the effect must survive.

onDestroy

onDestroy(callback: () => void): () => void;

Registers a callback fired when the host DestroyRef is destroyed. Returns a function that removes the registration.

RxEffectsSetupFn

export type RxEffectsSetupFn = (cfg: Pick<RxEffects, 'register' | 'onDestroy'>) => void;

RxEffectsOptions

export type RxEffectsOptions = {
injector?: Injector;
};
FieldTypeMeaning
injectorInjectorOptional. Explicit injector used to bind the instance's lifecycle, allowing rxEffects() to be called outside an injection context.

Minimal example

import { Component, inject } from '@angular/core';
import { rxEffects } from '@rx-angular/state/effects';
import { fromEvent } from 'rxjs';

@Component({
/* … */
})
export class MyComponent {
private readonly effects = rxEffects(({ register, onDestroy }) => {
register(fromEvent(window, 'resize'), () => {
console.log('window was resized');
});
onDestroy(() => {
console.log('custom cleanup logic');
});
});
}

rxEffects vs native effect()

rxEffects and Angular's built-in effect() both run side effects, but they consume different reactive sources. Pick by the shape of your trigger, and do not wrap one in the other to bridge.

Trigger sourceUseWhy
A signal() / computed() valueAngular effect()effect() tracks signal reads and re-runs on change; no subscription to manage.
An Observable (streams, events, HTTP, intervals)rxEffects().register(obs$, fn)Native effect() cannot subscribe to Observables; rxEffects owns the subscription and its cleanup.
A Promise / ObservableInput (fetch, scheduler actions)rxEffects().register(promise, fn)register accepts any ObservableInput; effect() does not consume Promises.
A signal you need as a streamrxEffects after toObservable(sig)Convert to an Observable at the boundary, then register; this keeps the effect in one paradigm.

Rule of thumb: signal in → effect(); Observable/Promise in → rxEffects. Reach for rxEffects when the trigger is asynchronous orchestration (multi-source merges, HTTP with retry/catchError, polling), and for effect() when reacting to already-signalized state.

Deprecated: the RxEffects class

import { RxEffects } from '@rx-angular/state/effects';

@deprecated: use rxEffects instead. The class-based RxEffects service is retained for backward compatibility only. New code should use the functional rxEffects creation function above.

The class is provided via providers: [RxEffects] and injected. Its surface differs from the functional handle:

MemberSignatureFunctional equivalent
registerregister<T>(sourceObs, sideEffectFn): number · register(sourceObs, observer): number · register(sideEffectObs): number · register(subscription): voidregister(...) returning an unregister callback (not a numeric id)
unregisterunregister(effectId: number): voidcall the callback returned by register(...)
registerOnDestroyregisterOnDestroy(sideEffect): number \| voidonDestroy(callback)
onDestroy$Observable<boolean>— (use onDestroy(...))
untilEffectuntilEffect(effectId) operatordropped; build your own (takeUntil(...)) if needed

The class register returns a numeric effect id; the functional register returns an unregister function, aligning with how Angular's effect() returns an EffectRef.

See also