CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-ngrx--store

RxJS powered Redux state management for Angular applications

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

store-service.mddocs/

Store Service

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.

Capabilities

Store Class

Main store service that manages application state with reactive capabilities.

/**
 * Injectable service that provides reactive state management and a public API for dispatching actions
 */
class Store<T = object> extends Observable<T> implements Observer<Action> {
  /** Current state as Angular Signal */
  readonly state: Signal<T>;
  
  /** Create signal from selector function */
  selectSignal<K>(
    selector: (state: T) => K,
    options?: SelectSignalOptions<K>
  ): Signal<K>;
  
  /** Dispatch action to update state */
  dispatch<V extends Action>(action: V & CreatorsNotAllowedCheck<V>): void;
  
  /** Dispatch function-based action with automatic effect management */
  dispatch<V extends () => Action>(
    dispatchFn: V & CreatorsNotAllowedCheck<V>,
    config?: { injector: Injector }
  ): EffectRef;
  
  /** Select state slice using function (Observable-based) */
  select<K>(mapFn: (state: T) => K): Observable<K>;
  
  /** Add reducer dynamically at runtime */
  addReducer<State, Actions extends Action = Action>(
    key: string,
    reducer: ActionReducer<State, Actions>
  ): void;
  
  /** Remove reducer dynamically at runtime */
  removeReducer<Key extends Extract<keyof T, string>>(key: Key): void;
  
  /** Lift operator for creating derived stores */
  lift<R>(operator: Operator<T, R>): Store<R>;
  
  /** Observer interface implementation */
  next(action: Action): void;
  error(err: any): void;
  complete(): void;
}

interface SelectSignalOptions<T> {
  /** Comparison function for equality checking */
  equal?: ValueEqualityFn<T>;
}

Usage Examples:

import { Component, inject } from "@angular/core";
import { Store } from "@ngrx/store";
import { selectUser, selectUserName } from "./user.selectors";
import { loadUser, updateUserName } from "./user.actions";

@Component({
  selector: 'app-user-profile',
  template: `
    <div>
      <h2>{{ userName() }}</h2>
      <button (click)="loadUserData()">Load User</button>
      <button (click)="updateName()">Update Name</button>
    </div>
  `
})
export class UserProfileComponent {
  private store = inject(Store);
  
  // Signal-based state selection (recommended)
  userName = this.store.selectSignal(selectUserName);
  user = this.store.selectSignal(selectUser);
  
  // Observable-based state selection
  user$ = this.store.select(selectUser);
  
  loadUserData() {
    this.store.dispatch(loadUser());
  }
  
  updateName() {
    this.store.dispatch(updateUserName({ name: 'New Name' }));
  }
  
  // Function-based dispatch with automatic effect management
  autoSaveUser() {
    const effectRef = this.store.dispatch(() => {
      const currentUser = this.user();
      if (currentUser?.isDirty) {
        return saveUser({ user: currentUser });
      }
      return noOp();
    });
    
    // Effect will run automatically when dependencies change
  }
}

Select Operator

Standalone operator function for use in RxJS pipelines.

/**
 * Selects state using a mapping function in RxJS pipelines
 */
function select<T, K>(mapFn: (state: T) => K): (source$: Observable<T>) => Observable<K>;

Usage Examples:

import { select } from "@ngrx/store";
import { pipe } from "rxjs";
import { map, distinctUntilChanged } from "rxjs/operators";

// Use in custom observables
const userAge$ = state$.pipe(
  select(state => state.user.age),
  map(age => age >= 18 ? 'adult' : 'minor')
);

// Compose with other operators  
const filteredUsers$ = state$.pipe(
  select(state => state.users),
  map(users => users.filter(user => user.active)),
  distinctUntilChanged()
);

Store Providers

Provider configuration for dependency injection.

/**
 * Provider array for Store service injection
 */
const STORE_PROVIDERS: Provider[];

Dynamic Reducer Management

The Store service supports dynamic addition and removal of reducers at runtime, enabling lazy loading and feature modules.

// Add reducer dynamically
store.addReducer('newFeature', newFeatureReducer);

// Remove reducer when feature is unloaded
store.removeReducer('oldFeature');

Signal Integration

The Store service provides modern Angular Signals support through selectSignal():

// Create computed signals from state
const userName = store.selectSignal(state => state.user.name);
const isLoggedIn = store.selectSignal(state => !!state.user.id);

// Use in templates
template: `<div>Welcome {{ userName() }}!</div>`

// Custom equality checking
const complexData = store.selectSignal(
  state => state.complexObject,
  { equal: (a, b) => a.id === b.id && a.version === b.version }
);

Function-based Dispatch

Modern reactive dispatch pattern that automatically manages effects:

// Traditional dispatch
store.dispatch(someAction());

// Function-based dispatch with automatic effect management
const effectRef = store.dispatch(() => {
  // This function runs reactively when dependencies change
  const currentState = store.state();
  return currentState.shouldUpdate ? updateAction() : noOpAction();
});

// Effect automatically manages its lifecycle
// Call effectRef.destroy() to clean up if needed

Core Services

Actions Subject

Injectable service that extends BehaviorSubject to manage action dispatching with validation.

/**
 * Injectable service that dispatches actions and validates them before emission
 */
class ActionsSubject extends BehaviorSubject<Action> implements OnDestroy {
  /** Dispatches action with validation */
  next(action: Action): void;
  /** Completes the subject (no-op for actions) */
  complete(): void;
  /** Angular lifecycle hook for cleanup */
  ngOnDestroy(): void;
}

/** Initial action type dispatched when store initializes */
const INIT: '@ngrx/store/init';

/** Provider configuration for ActionsSubject */
const ACTIONS_SUBJECT_PROVIDERS: Provider[];

Usage Examples:

import { inject } from "@angular/core";
import { ActionsSubject, INIT } from "@ngrx/store";
import { filter } from "rxjs/operators";

@Injectable()
export class ActionLogger {
  private actions$ = inject(ActionsSubject);

  constructor() {
    // Listen to all actions
    this.actions$.pipe(
      filter(action => action.type !== INIT)
    ).subscribe(action => {
      console.log('Action dispatched:', action.type, action);
    });
  }
}

// Custom action monitoring
const customActionMonitor = inject(ActionsSubject).pipe(
  filter(action => action.type.startsWith('[User]'))
).subscribe(userAction => {
  // Handle user-specific actions
  handleUserAction(userAction);
});

Reducer Manager

Service that manages dynamic reducer registration and composition.

/**
 * Injectable service for dynamic reducer management
 */
class ReducerManager extends BehaviorSubject<ActionReducer<any, any>> implements OnDestroy {
  /** Current reducer map */
  get currentReducers(): ActionReducerMap<any, any>;
  
  /** Add feature with reducers and configuration */
  addFeature(feature: StoreFeature<any, any>): void;
  addFeatures(features: StoreFeature<any, any>[]): void;
  
  /** Remove feature by reference */
  removeFeature(feature: StoreFeature<any, any>): void;
  removeFeatures(features: StoreFeature<any, any>[]): void;
  
  /** Add single reducer */
  addReducer(key: string, reducer: ActionReducer<any, any>): void;
  addReducers(reducers: ActionReducerMap<any, any>): void;
  
  /** Remove reducer by key */
  removeReducer(key: string): void;
  removeReducers(featureKeys: string[]): void;
  
  /** Angular lifecycle hook */
  ngOnDestroy(): void;
}

/** Abstract base class for reducer observables */
abstract class ReducerObservable extends Observable<ActionReducer<any, any>> {}

/** Abstract dispatcher for reducer manager actions */
abstract class ReducerManagerDispatcher extends ActionsSubject {}

/** Action type for reducer updates */
const UPDATE: '@ngrx/store/update-reducers';

Usage Examples:

import { inject } from "@angular/core";
import { ReducerManager } from "@ngrx/store";

@Injectable()
export class DynamicFeatureService {
  private reducerManager = inject(ReducerManager);

  loadFeature(featureName: string) {
    // Dynamically add feature reducer
    this.reducerManager.addReducer(featureName, featureReducer);
    
    // Add complete feature configuration
    this.reducerManager.addFeature({
      key: featureName,
      reducers: featureReducerMap,
      reducerFactory: combineReducers,
      initialState: featureInitialState,
      metaReducers: [loggingMetaReducer]
    });
  }

  unloadFeature(featureName: string) {
    // Remove reducer when feature is no longer needed
    this.reducerManager.removeReducer(featureName);
  }

  // Monitor reducer changes
  trackReducerChanges() {
    this.reducerManager.subscribe(currentReducer => {
      console.log('Root reducer updated');
    });
  }
}

State Service

Core state management service with Signal integration.

/**
 * Core state service that manages the application state tree
 */
class State<T> extends BehaviorSubject<T> implements OnDestroy {
  /** Current state as Angular Signal */
  readonly state: Signal<T>;
  
  /** Static reference to INIT action */
  static readonly INIT: '@ngrx/store/init';
  
  /** Angular lifecycle hook */
  ngOnDestroy(): void;
}

/** Abstract base class for state observables */
abstract class StateObservable extends Observable<any> {
  /** Current state as Angular Signal */
  abstract readonly state: Signal<any>;
}

/** Reduces state with given action using current reducer */
function reduceState<T, V extends Action = Action>(
  state: T | undefined,
  action: V
): T;

Usage Examples:

import { inject, Signal } from "@angular/core";
import { State, StateObservable } from "@ngrx/store";

@Injectable()
export class StateMonitor {
  private state = inject(State);

  // Access state as Signal
  getCurrentState(): Signal<any> {
    return this.state.state;
  }

  // Subscribe to state changes
  watchStateChanges() {
    this.state.subscribe(currentState => {
      console.log('State updated:', currentState);
    });
  }

  // Get specific state slice
  getUserState() {
    return this.state.pipe(
      map(state => state.user),
      distinctUntilChanged()
    );
  }
}

// Custom state observable
@Injectable()
export class CustomStateObservable extends StateObservable {
  readonly state = inject(State).state;

  constructor() {
    super(subscriber => {
      // Custom state emission logic
      this.state().subscribe(subscriber);
    });
  }
}

Scanned Actions Subject

Service that tracks processed actions for debugging and development tools.

/**
 * Subject that emits actions after they have been processed by reducers
 */
class ScannedActionsSubject extends Subject<Action> {
  /** Emits processed action */
  next(action: Action): void;
}

Usage Examples:

import { inject } from "@angular/core";
import { ScannedActionsSubject } from "@ngrx/store";

@Injectable()
export class DevToolsService {
  private scannedActions$ = inject(ScannedActionsSubject);

  constructor() {
    // Track processed actions for debugging
    this.scannedActions$.subscribe(processedAction => {
      console.log('Action processed:', processedAction.type);
      this.sendToDevTools(processedAction);
    });
  }

  private sendToDevTools(action: Action) {
    // Send to Redux DevTools or custom debugging tools
    if (window.__REDUX_DEVTOOLS_EXTENSION__) {
      window.__REDUX_DEVTOOLS_EXTENSION__.send(action);
    }
  }
}

docs

action-creators.md

feature-management.md

index.md

module-configuration.md

reducer-creators.md

selectors.md

standalone-providers.md

store-service.md

testing-utilities.md

tile.json