RxJS powered Redux state management for Angular applications
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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.
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()
}))
);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
);/**
* 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>;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()
}
}))
);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)
);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
}))
};
})
);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' }
});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);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
});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
);compose for clean meta-reducer composition