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

reducer-creators.mddocs/

Reducer Creators

Reducer creators provide a simplified, type-safe way to create reducers using the on function pattern instead of traditional switch statements. They automatically handle action type matching and provide full TypeScript support.

Capabilities

Create Reducer

Creates a reducer function using action handlers defined with the on function.

/**
 * Creates a reducer function to handle state transitions
 * @param initialState - Initial state value when state is undefined
 * @param ons - Action handlers created with the on() function
 * @returns ActionReducer function for state management
 */
function createReducer<State>(
  initialState: State,
  ...ons: ReducerTypes<State, any>[]
): ActionReducer<State>;

Usage Examples:

import { createReducer, on } from "@ngrx/store";
import { increment, decrement, reset, setValue } from "./counter.actions";

interface CounterState {
  count: number;
  lastUpdated: number;
}

const initialState: CounterState = {
  count: 0,
  lastUpdated: Date.now()
};

// Create reducer with action handlers
export const counterReducer = createReducer(
  initialState,
  
  // Simple state update
  on(increment, (state) => ({
    ...state,
    count: state.count + 1,
    lastUpdated: Date.now()
  })),
  
  on(decrement, (state) => ({
    ...state,
    count: state.count - 1,
    lastUpdated: Date.now()
  })),
  
  // Reset to initial state
  on(reset, () => initialState),
  
  // Handle action with payload
  on(setValue, (state, { value }) => ({
    ...state,
    count: value,
    lastUpdated: Date.now()
  })),
  
  // Handle multiple actions with same logic
  on(clearCounter, resetCounter, () => ({
    ...initialState,
    lastUpdated: Date.now()
  }))
);

On Function

Associates action creators with their corresponding state change functions.

/**
 * Associates actions with a given state change function
 * @param args - ActionCreator(s) followed by a state change function
 * @returns Association of action types with state change function
 */
function on<State, Creators extends readonly ActionCreator[], InferredState = State>(
  ...args: [
    ...creators: Creators,
    reducer: OnReducer<State extends infer S ? S : never, Creators, InferredState>
  ]
): ReducerTypes<unknown extends State ? InferredState : State, Creators>;

/**
 * Specialized reducer that is aware of the Action type it needs to handle
 */
interface OnReducer<State, Creators extends readonly ActionCreator[], InferredState = State, ResultState = unknown extends State ? InferredState : State> {
  (
    state: unknown extends State ? InferredState : State,
    action: ActionType<Creators[number]>
  ): ResultState;
}

/**
 * Return type of the on() function containing the reducer and action types
 */
interface ReducerTypes<State, Creators extends readonly ActionCreator[]> {
  reducer: OnReducer<State, Creators>;
  types: ExtractActionTypes<Creators>;
}

Usage Examples:

import { on } from "@ngrx/store";
import { loadUser, loadUserSuccess, loadUserFailure } from "./user.actions";

// Single action handler
const userLoadingHandler = on(loadUser, (state) => ({
  ...state,
  loading: true,
  error: null
}));

// Multiple actions with same handler
const userResetHandler = on(
  resetUser, 
  clearUserData, 
  logoutUser,
  (state) => ({
    ...state,
    user: null,
    loading: false,
    error: null
  })
);

// Action with payload
const userSuccessHandler = on(
  loadUserSuccess,
  (state, { user, timestamp }) => ({
    ...state,
    user,
    loading: false,
    error: null,
    lastUpdated: timestamp
  })
);

// Error handling
const userErrorHandler = on(
  loadUserFailure,
  (state, { error }) => ({
    ...state,
    loading: false,
    error: error.message
  })
);

// Use in reducer
export const userReducer = createReducer(
  initialUserState,
  userLoadingHandler,
  userSuccessHandler,
  userErrorHandler,
  userResetHandler
);

Core Type Definitions

Action Reducer Types

/**
 * Function that takes an Action and a State, and returns a State
 */
interface ActionReducer<T, V extends Action = Action> {
  (state: T | undefined, action: V): T;
}

/**
 * Map of state keys to their corresponding reducers
 */
type ActionReducerMap<T, V extends Action = Action> = {
  [p in keyof T]: ActionReducer<T[p], V>;
};

/**
 * Factory for creating action reducers
 */
interface ActionReducerFactory<T, V extends Action = Action> {
  (
    reducerMap: ActionReducerMap<T, V>,
    initialState?: InitialState<T>
  ): ActionReducer<T, V>;
}

/**
 * Higher-order reducer that wraps other reducers
 */
type MetaReducer<T = any, V extends Action = Action> = (
  reducer: ActionReducer<T, V>
) => ActionReducer<T, V>;

Advanced Usage Patterns

Nested State Updates

import { createReducer, on } from "@ngrx/store";
import { updateUserProfile, updateUserPreferences } from "./user.actions";

interface UserState {
  profile: {
    name: string;
    email: string;
    avatar: string;
  };
  preferences: {
    theme: 'light' | 'dark';
    notifications: boolean;
    language: string;
  };
  metadata: {
    lastLogin: number;
    loginCount: number;
  };
}

export const userReducer = createReducer(
  initialUserState,
  
  // Nested state update
  on(updateUserProfile, (state, { profile }) => ({
    ...state,
    profile: {
      ...state.profile,
      ...profile
    }
  })),
  
  // Deep nested update
  on(updateUserPreferences, (state, { preferences }) => ({
    ...state,
    preferences: {
      ...state.preferences,
      ...preferences
    },
    metadata: {
      ...state.metadata,
      lastUpdated: Date.now()
    }
  }))
);

Conditional State Updates

export const gameReducer = createReducer(
  initialGameState,
  
  on(makeMove, (state, { move }) => {
    // Conditional logic in reducer
    if (state.gameOver) {
      return state; // No changes if game is over
    }
    
    const newBoard = applyMove(state.board, move);
    const isGameOver = checkGameOver(newBoard);
    
    return {
      ...state,
      board: newBoard,
      currentPlayer: state.currentPlayer === 'X' ? 'O' : 'X',
      gameOver: isGameOver,
      winner: isGameOver ? getWinner(newBoard) : null,
      moves: [...state.moves, move]
    };
  }),
  
  on(resetGame, () => initialGameState)
);

Array State Management

export const todosReducer = createReducer(
  initialTodosState,
  
  // Add item to array
  on(addTodo, (state, { todo }) => ({
    ...state,
    todos: [...state.todos, { ...todo, id: generateId() }]
  })),
  
  // Update item in array
  on(updateTodo, (state, { id, changes }) => ({
    ...state,
    todos: state.todos.map(todo =>
      todo.id === id ? { ...todo, ...changes } : todo
    )
  })),
  
  // Remove item from array
  on(deleteTodo, (state, { id }) => ({
    ...state,
    todos: state.todos.filter(todo => todo.id !== id)
  })),
  
  // Bulk operations
  on(toggleAllTodos, (state) => {
    const allCompleted = state.todos.every(todo => todo.completed);
    return {
      ...state,
      todos: state.todos.map(todo => ({
        ...todo,
        completed: !allCompleted
      }))
    };
  })
);

Utility Functions

Combine Reducers

Combines multiple reducers into a single reducer function for managing different parts of the state tree.

/**
 * Combines reducers for individual features into a single reducer
 * @param reducers - Object mapping keys of the root state to their corresponding feature reducer
 * @param initialState - Provides a state value if the current state is undefined
 * @returns A reducer function that delegates to feature reducers
 */
function combineReducers<T, V extends Action = Action>(
  reducers: ActionReducerMap<T, V>,
  initialState?: Partial<T>
): ActionReducer<T, V>;

Usage Examples:

import { combineReducers } from "@ngrx/store";
import { counterReducer } from "./counter.reducer";
import { userReducer } from "./user.reducer";
import { todosReducer } from "./todos.reducer";

// Combine feature reducers into root reducer
export const rootReducer = combineReducers({
  counter: counterReducer,
  user: userReducer,
  todos: todosReducer
});

// With initial state override
export const rootReducerWithDefaults = combineReducers({
  counter: counterReducer,
  user: userReducer,
  todos: todosReducer
}, {
  counter: { count: 10 },
  user: { isLoggedIn: false },
  todos: { items: [], filter: 'all' }
});

Compose

Function composition utility for combining multiple functions, commonly used with meta-reducers.

/**
 * Composes multiple functions into a single function
 * @param functions - Functions to compose (applied right to left)
 * @returns Composed function that applies all input functions in sequence
 */
function compose<A>(): (i: A) => A;
function compose<A, B>(b: (i: A) => B): (i: A) => B;
function compose<A, B, C>(c: (i: B) => C, b: (i: A) => B): (i: A) => C;
function compose<A, B, C, D>(
  d: (i: C) => D,
  c: (i: B) => C,
  b: (i: A) => B
): (i: A) => D;
function compose<A, B, C, D, E>(
  e: (i: D) => E,
  d: (i: C) => D,
  c: (i: B) => C,
  b: (i: A) => B
): (i: A) => E;
function compose<A, B, C, D, E, F>(
  f: (i: E) => F,
  e: (i: D) => E,
  d: (i: C) => D,
  c: (i: B) => C,
  b: (i: A) => B
): (i: A) => F;
function compose<A = any, F = any>(...functions: any[]): (i: A) => F;

Usage Examples:

import { compose, MetaReducer } from "@ngrx/store";

// Create meta-reducers
const loggingMetaReducer: MetaReducer<any> = (reducer) => (state, action) => {
  console.log('Action:', action.type);
  const nextState = reducer(state, action);
  console.log('Next State:', nextState);
  return nextState;
};

const freezeMetaReducer: MetaReducer<any> = (reducer) => (state, action) => {
  return Object.freeze(reducer(state, action));
};

// Compose meta-reducers
const composedMetaReducer = compose(
  freezeMetaReducer,
  loggingMetaReducer
);

// Use with reducer factory
const enhancedReducer = composedMetaReducer(baseReducer);

Create Reducer Factory

Creates a reducer factory that can apply meta-reducers to a collection of reducers.

/**
 * Creates a reducer factory with optional meta-reducers
 * @param reducerFactory - Factory function for creating reducers
 * @param metaReducers - Array of meta-reducers to apply
 * @returns Enhanced reducer factory with meta-reducers applied
 */
function createReducerFactory<T, V extends Action = Action>(
  reducerFactory: ActionReducerFactory<T, V>,
  metaReducers?: MetaReducer<T, V>[]
): ActionReducerFactory<T, V>;

Usage Examples:

import { createReducerFactory, combineReducers, MetaReducer } from "@ngrx/store";

// Create meta-reducers
const loggerMetaReducer: MetaReducer<any> = (reducer) => (state, action) => {
  console.log(`[${action.type}]`, state);
  return reducer(state, action);
};

const immutabilityMetaReducer: MetaReducer<any> = (reducer) => (state, action) => {
  const result = reducer(state, action);
  return Object.freeze(result);
};

// Create enhanced reducer factory
const enhancedReducerFactory = createReducerFactory(
  combineReducers,
  [loggerMetaReducer, immutabilityMetaReducer]
);

// Use factory to create root reducer with meta-reducers applied
const rootReducer = enhancedReducerFactory({
  counter: counterReducer,
  user: userReducer,
  todos: todosReducer
});

Create Feature Reducer Factory

Creates a factory for feature reducers with meta-reducer support.

/**
 * Creates a feature reducer factory with optional meta-reducers
 * @param metaReducers - Array of meta-reducers to apply to feature reducers
 * @returns Factory function for creating feature reducers with meta-reducers
 */
function createFeatureReducerFactory<T, V extends Action = Action>(
  metaReducers?: MetaReducer<T, V>[]
): (reducer: ActionReducer<T, V>, initialState?: T) => ActionReducer<T, V>;

Usage Examples:

import { createFeatureReducerFactory, MetaReducer } from "@ngrx/store";

// Create feature-specific meta-reducers
const featureLoggerMetaReducer: MetaReducer<any> = (reducer) => (state, action) => {
  if (action.type.startsWith('[Feature]')) {
    console.log('Feature action:', action.type, state);
  }
  return reducer(state, action);
};

// Create feature reducer factory
const featureReducerFactory = createFeatureReducerFactory([
  featureLoggerMetaReducer
]);

// Use factory for feature reducers
const enhancedFeatureReducer = featureReducerFactory(
  baseFeatureReducer,
  initialFeatureState
);

Best Practices

  1. Immutable Updates: Always return new state objects, never mutate existing state
  2. Type Safety: Use TypeScript interfaces for state and action payloads
  3. Pure Functions: Reducers should be pure functions without side effects
  4. Minimal Logic: Keep complex logic in services/effects, not reducers
  5. Consistent Structure: Use consistent state shape and update patterns
  6. Error Handling: Handle error actions to maintain application stability
  7. Meta-Reducers: Use meta-reducers for cross-cutting concerns like logging, immutability checks
  8. Composition: Leverage compose for clean meta-reducer composition

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