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

selectors.mddocs/

Selectors

Selectors provide efficient, memoized state selection with composition capabilities. They enable derived state computation, performance optimization through memoization, and type-safe state access patterns.

Capabilities

Create Selector

Creates memoized selectors that efficiently compute derived state with automatic memoization and composition support.

/**
 * Creates a memoized selector from input selectors and projector function
 * @param selectors - Array of input selectors
 * @param projector - Function that computes the final result
 * @returns MemoizedSelector with memoization and lifecycle methods
 */
function createSelector<Selectors extends readonly any[], Result>(
  selectors: [...Selectors],
  projector: (...args: SelectorResults<Selectors>) => Result
): MemoizedSelector<any, Result>;

interface MemoizedSelector<State, Result, ProjectorFn = DefaultProjectorFn<Result>>
  extends Selector<State, Result> {
  /** Release memoized values and reset internal state */
  release(): void;
  /** The projector function used to compute the result */
  projector: ProjectorFn;
  /** Override the result value (useful for testing) */
  setResult: (result?: Result) => void;
  /** Clear any overridden result value */
  clearResult: () => void;
}

Usage Examples:

import { createSelector } from "@ngrx/store";

interface AppState {
  users: UserState;
  orders: OrderState;
  ui: UiState;
}

// Feature selectors
const selectUserState = (state: AppState) => state.users;
const selectOrderState = (state: AppState) => state.orders;

// Basic selector
const selectAllUsers = createSelector(
  selectUserState,
  (userState) => userState.entities
);

// Composed selector with multiple inputs
const selectActiveUserOrders = createSelector(
  selectAllUsers,
  selectOrderState,
  (users, orderState, props: { userId: string }) => {
    const user = users.find(u => u.id === props.userId);
    return user?.active ? orderState.entities.filter(o => o.userId === props.userId) : [];
  }
);

// Complex derived state
const selectOrderSummary = createSelector(
  selectOrderState,
  selectAllUsers,
  (orderState, users) => {
    const orders = orderState.entities;
    const totalRevenue = orders.reduce((sum, order) => sum + order.total, 0);
    const ordersByStatus = orders.reduce((acc, order) => {
      acc[order.status] = (acc[order.status] || 0) + 1;
      return acc;
    }, {} as Record<string, number>);
    
    return {
      totalOrders: orders.length,
      totalRevenue,
      averageOrderValue: totalRevenue / orders.length || 0,
      ordersByStatus,
      topCustomers: users
        .map(user => ({
          ...user,
          orderCount: orders.filter(o => o.userId === user.id).length
        }))
        .sort((a, b) => b.orderCount - a.orderCount)
        .slice(0, 5)
    };
  }
);

Create Feature Selector

Creates selectors for accessing feature state slices in the store.

/**
 * Creates a selector for a specific feature slice of state
 * @param featureKey - The key of the feature in the state
 * @returns MemoizedSelector for the feature state
 */
function createFeatureSelector<T, K extends keyof T>(
  featureKey: K
): MemoizedSelector<T, T[K]>;

Usage Examples:

import { createFeatureSelector, createSelector } from "@ngrx/store";

// Create feature selectors
const selectUserFeature = createFeatureSelector<AppState, 'users'>('users');
const selectOrderFeature = createFeatureSelector<AppState, 'orders'>('orders');
const selectUiFeature = createFeatureSelector<AppState, 'ui'>('ui');

// Use feature selectors as base for other selectors
const selectUserList = createSelector(
  selectUserFeature,
  (userState) => userState.entities
);

const selectCurrentUser = createSelector(
  selectUserFeature,
  (userState) => userState.entities.find(u => u.id === userState.selectedUserId)
);

const selectIsLoading = createSelector(
  selectUserFeature,
  selectOrderFeature,
  (userState, orderState) => userState.loading || orderState.loading
);

Create Selector Factory

Creates custom selector factories with configurable memoization strategies.

/**
 * Creates a selector factory with custom memoization
 * @param memoize - Custom memoization function
 * @returns SelectorFactory for creating selectors with custom memoization
 */
function createSelectorFactory<T, V>(memoize: MemoizeFn): SelectorFactory<T, V>;

/**
 * Function that adds memoization to any function
 */
type MemoizeFn = (fn: AnyFn) => MemoizedProjection;

/**
 * Comparison function for memoization
 */
type ComparatorFn = (a: any, b: any) => boolean;

interface MemoizedProjection {
  memoized: AnyFn;
  reset: () => void;
  setResult: (result?: any) => void;
  clearResult: () => void;
}

Usage Examples:

import { createSelectorFactory, defaultMemoize, resultMemoize } from "@ngrx/store";

// Custom memoization that only checks result equality
const createDeepEqualSelector = createSelectorFactory(
  (projectionFn) => resultMemoize(projectionFn, deepEqual)
);

// Selector with custom memoization
const selectComplexData = createDeepEqualSelector(
  selectUserState,
  selectOrderState,
  (userState, orderState) => {
    // Expensive computation that returns complex object
    return computeComplexAnalytics(userState, orderState);
  }
);

// No memoization selector for always-fresh data
const createNonMemoizedSelector = createSelectorFactory(
  (projectionFn) => ({
    memoized: projectionFn,
    reset: () => {},
    setResult: () => {},
    clearResult: () => {}
  })
);

const selectCurrentTime = createNonMemoizedSelector(
  selectUiState,
  (uiState) => ({ 
    ...uiState.timeConfig, 
    currentTime: Date.now() 
  })
);

Default Memoization Functions

Built-in memoization strategies for different use cases.

/**
 * Default memoization function with argument and result equality checking
 * @param projectionFn - Function to memoize
 * @param isArgumentsEqual - Function to compare arguments (default: strict equality)
 * @param isResultEqual - Function to compare results (default: strict equality)
 * @returns MemoizedProjection with reset and override capabilities
 */
function defaultMemoize(
  projectionFn: AnyFn,
  isArgumentsEqual?: ComparatorFn,
  isResultEqual?: ComparatorFn
): MemoizedProjection;

/**
 * Result-based memoization that only checks result equality
 * @param projectionFn - Function to memoize
 * @param isResultEqual - Function to compare results
 * @returns MemoizedProjection focused on result comparison
 */
function resultMemoize(
  projectionFn: AnyFn,
  isResultEqual: ComparatorFn
): MemoizedProjection;

/**
 * Default state function (identity function)
 */
function defaultStateFn<T>(state: T): T;

/**
 * Default equality check using strict equality
 */
function isEqualCheck(a: any, b: any): boolean;

Core Type Definitions

Selector Types

/**
 * Function that selects a value from state
 */
type Selector<T, V> = (state: T) => V;

/**
 * @deprecated Selector with additional props parameter
 */
type SelectorWithProps<State, Props, Result> = (state: State, props: Props) => Result;

/**
 * Default projector function type
 */
type DefaultProjectorFn<T> = (...args: any[]) => T;

/**
 * Any function type for memoization
 */
type AnyFn = (...args: any[]) => any;

Advanced Usage Patterns

Performance-Optimized Selectors

import { createSelector, createSelectorFactory, defaultMemoize } from "@ngrx/store";

// Custom memoization for expensive computations
const createExpensiveSelector = createSelectorFactory(
  (projectionFn) => defaultMemoize(
    projectionFn,
    // Custom argument equality - only recompute if IDs change
    (argsA, argsB) => {
      return argsA[0]?.id === argsB[0]?.id && argsA[1]?.version === argsB[1]?.version;
    },
    // Custom result equality - deep comparison
    (a, b) => JSON.stringify(a) === JSON.stringify(b)
  )
);

const selectExpensiveCalculation = createExpensiveSelector(
  selectLargeDataset,
  selectConfiguration,
  (dataset, config) => {
    // Expensive computation
    return performComplexAnalysis(dataset, config);
  }
);

Parameterized Selectors

// Selector factory pattern for parameterized selection
const makeSelectUserById = () => createSelector(
  selectAllUsers,
  (users, props: { id: string }) => users.find(user => user.id === props.id)
);

const makeSelectUserOrders = () => createSelector(
  selectAllOrders,
  (orders, props: { userId: string }) => orders.filter(order => order.userId === props.userId)
);

// Usage in components
class UserComponent {
  @Input() userId!: string;
  
  private selectUser = makeSelectUserById();
  private selectOrders = makeSelectUserOrders();
  
  user$ = this.store.select(this.selectUser, { id: this.userId });
  orders$ = this.store.select(this.selectOrders, { userId: this.userId });
}

Conditional Selectors

const selectConditionalData = createSelector(
  selectUserState,
  selectAppConfig,
  (userState, config) => {
    // Conditional logic in selector
    if (config.featureFlags.advancedMode) {
      return {
        ...userState,
        advancedMetrics: computeAdvancedMetrics(userState.entities),
        recommendations: generateRecommendations(userState.entities)
      };
    }
    
    return {
      basicData: userState.entities.map(user => ({
        id: user.id,
        name: user.name,
        status: user.status
      }))
    };
  }
);

Selector Composition and Reuse

// Base selectors
const selectActiveUsers = createSelector(
  selectAllUsers,
  users => users.filter(user => user.active)
);

const selectPremiumUsers = createSelector(
  selectAllUsers,
  users => users.filter(user => user.subscription === 'premium')
);

// Composed selectors
const selectActivePremiumUsers = createSelector(
  selectActiveUsers,
  selectPremiumUsers,
  (activeUsers, premiumUsers) => 
    activeUsers.filter(user => premiumUsers.some(p => p.id === user.id))
);

const selectUserStatistics = createSelector(
  selectAllUsers,
  selectActiveUsers,
  selectPremiumUsers,
  (allUsers, activeUsers, premiumUsers) => ({
    total: allUsers.length,
    active: activeUsers.length,
    premium: premiumUsers.length,
    activeRatio: activeUsers.length / allUsers.length,
    premiumRatio: premiumUsers.length / allUsers.length
  })
);

Testing Selectors

import { selectUserSummary } from './user.selectors';

describe('User Selectors', () => {
  it('should select user summary', () => {
    const mockState = {
      users: {
        entities: [
          { id: '1', name: 'John', active: true },
          { id: '2', name: 'Jane', active: false }
        ]
      }
    };
    
    const result = selectUserSummary(mockState);
    
    expect(result).toEqual({
      total: 2,
      active: 1,
      inactive: 1
    });
  });
  
  it('should memoize results', () => {
    const state1 = { users: { entities: [] } };
    const state2 = { users: { entities: [] } };
    
    const result1 = selectUserSummary(state1);
    const result2 = selectUserSummary(state2);
    
    // Same reference due to memoization
    expect(result1).toBe(result2);
  });
});

Best Practices

  1. Use Feature Selectors: Start with createFeatureSelector for clear state structure
  2. Compose Selectors: Build complex selectors from simpler ones for reusability
  3. Memoization Strategy: Choose appropriate memoization based on data characteristics
  4. Pure Functions: Keep selector functions pure and side-effect free
  5. Performance: Use custom memoization for expensive computations
  6. Testing: Test selectors independently with mock state data

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