Selectors for Redux - memoized functions for computing derived data from state.
—
Core functionality for creating memoized selectors using the default createSelector function and customizable options.
Creates a memoized selector that computes derived data from state. The selector will only recompute when one of its input selectors returns a different value.
/**
* Creates a memoized selector from input selectors and a result function
* @param selectors - Array of input selector functions
* @param combiner - Function that combines the results of input selectors
* @returns Memoized output selector with additional metadata
*/
function createSelector<State, Result>(
selectors: SelectorArray<State>,
combiner: (...args: any[]) => Result
): OutputSelector<State, Result>;
/**
* Creates a memoized selector with custom options
* @param selectors - Array of input selector functions
* @param combiner - Function that combines the results of input selectors
* @param options - Configuration options for memoization and dev mode checks
* @returns Memoized output selector with additional metadata
*/
function createSelector<State, Result>(
selectors: SelectorArray<State>,
combiner: (...args: any[]) => Result,
options: CreateSelectorOptions
): OutputSelector<State, Result>;
/**
* Variadic overload for inline input selectors
* @param selector1 - First input selector
* @param selector2 - Second input selector
* @param combiner - Function that combines selector results
* @returns Memoized output selector
*/
function createSelector<State, Res1, Res2, Result>(
selector1: Selector<State, Res1>,
selector2: Selector<State, Res2>,
combiner: (res1: Res1, res2: Res2) => Result
): OutputSelector<State, Result>;
// Additional overloads for 3-12 input selectors...Basic Usage:
import { createSelector } from "reselect";
// Simple selector with two inputs
const selectShopItems = (state) => state.shop.items;
const selectTaxPercent = (state) => state.shop.taxPercent;
const selectSubtotal = createSelector(
[selectShopItems],
(items) => items.reduce((acc, item) => acc + item.price, 0)
);
const selectTax = createSelector(
[selectSubtotal, selectTaxPercent],
(subtotal, taxPercent) => subtotal * (taxPercent / 100)
);
const selectTotal = createSelector(
[selectSubtotal, selectTax],
(subtotal, tax) => ({ total: subtotal + tax })
);With Custom Options:
import { createSelector, lruMemoize } from "reselect";
const selectExpensiveComputation = createSelector(
[selectLargeDataset],
(data) => computeExpensiveDerivation(data),
{
memoize: lruMemoize,
memoizeOptions: { maxSize: 10 },
devModeChecks: { inputStabilityCheck: 'always' }
}
);The enhanced selector returned by createSelector with additional metadata and methods.
interface OutputSelector<State, Result, Params extends readonly any[] = any[]>
extends Selector<State, Result, Params> {
/** The original result function passed to createSelector */
resultFunc: (...args: any[]) => Result;
/** The memoization function used by this selector */
memoize: UnknownMemoizer;
/** The arguments memoization function used by this selector */
argsMemoize: UnknownMemoizer;
/** Array of input selectors used by this selector */
dependencies: SelectorArray<State>;
/** Returns the number of times the result function has been recomputed */
recomputations: () => number;
/** Resets the recomputation counter to 0 */
resetRecomputations: () => void;
/** Returns the number of times the dependencies have been recomputed */
dependencyRecomputations: () => number;
/** Resets the dependency recomputation counter to 0 */
resetDependencyRecomputations: () => void;
/** Returns the last computed result */
lastResult: () => Result;
/** The memoized version of the result function */
memoizedResultFunc: (...args: any[]) => Result;
}Usage Examples:
const mySelector = createSelector([selectA, selectB], (a, b) => a + b);
// Check recomputation counts
console.log(mySelector.recomputations()); // 0
console.log(mySelector.dependencyRecomputations()); // 0
// Use the selector
const result1 = mySelector(state);
console.log(mySelector.recomputations()); // 1
console.log(mySelector.dependencyRecomputations()); // 1
// Same inputs won't recompute
const result2 = mySelector(state);
console.log(mySelector.recomputations()); // Still 1
console.log(mySelector.dependencyRecomputations()); // Still 1
// Get the last result
console.log(mySelector.lastResult()); // The computed result
// Reset counters
mySelector.resetRecomputations();
mySelector.resetDependencyRecomputations();
console.log(mySelector.recomputations()); // 0
console.log(mySelector.dependencyRecomputations()); // 0
// Access the underlying result function
const directResult = mySelector.resultFunc(valueA, valueB);Configuration options for customizing selector behavior.
interface CreateSelectorOptions<MemoizeFunction = typeof weakMapMemoize> {
/** Custom memoization function (defaults to weakMapMemoize) */
memoize?: MemoizeFunction;
/** Options passed to the memoization function */
memoizeOptions?: ExtractMemoizerFields<MemoizeFunction>;
/** Custom arguments memoization function (defaults to weakMapMemoize) */
argsMemoize?: UnknownMemoizer;
/** Options passed to the arguments memoization function */
argsMemoizeOptions?: unknown[];
/** Development mode check configuration */
devModeChecks?: Partial<DevModeChecks>;
}
interface DevModeChecks {
/** Check for unstable input selector results */
inputStabilityCheck: DevModeCheckFrequency;
/** Check if result function is an identity function */
identityFunctionCheck: DevModeCheckFrequency;
}
type DevModeCheckFrequency = 'once' | 'always' | 'never';Selectors that accept additional parameters beyond state.
import { createSelector } from "reselect";
// Selector with parameters
const selectTodoById = createSelector(
[(state, id) => state.todos, (state, id) => id],
(todos, id) => todos.find(todo => todo.id === id)
);
// Usage with parameters
const todo = selectTodoById(state, 42);
// Factory pattern for parametric selectors
const makeSelectorTodosByStatus = () => createSelector(
[(state, status) => state.todos, (state, status) => status],
(todos, status) => todos.filter(todo => todo.status === status)
);
const selectCompletedTodos = makeSelectorTodosByStatus();
const completedTodos = selectCompletedTodos(state, 'completed');Create pre-typed versions of createSelector with predefined state types to eliminate repetitive type annotations.
import { createSelector } from "reselect";
interface RootState {
todos: { id: number; completed: boolean }[];
alerts: { id: number; read: boolean }[];
}
// Create a pre-typed createSelector with RootState
export const createAppSelector = createSelector.withTypes<RootState>();
// Now you can use createAppSelector without specifying state types
const selectTodoIds = createAppSelector(
[
// Type of `state` is automatically set to `RootState`
state => state.todos
],
todos => todos.map(({ id }) => id)
);
const selectActiveAlerts = createAppSelector(
[state => state.alerts],
alerts => alerts.filter(alert => !alert.read)
);type Selector<State = any, Result = unknown, Params extends readonly any[] = any[]> =
(state: State, ...params: Params) => Result;
type SelectorArray<State = any> = readonly Selector<State>[];
type SelectorResultArray<Selectors extends SelectorArray> = {
[Index in keyof Selectors]: Selectors[Index] extends Selector<any, infer Result>
? Result
: never;
};
type Combiner<Result> = (...args: any[]) => Result;
type UnknownMemoizer = (func: AnyFunction, ...options: unknown[]) => AnyFunction;
type AnyFunction = (...args: any[]) => any;Install with Tessl CLI
npx tessl i tessl/npm-reselect