Selectors for Redux - memoized functions for computing derived data from state.
—
Utility for creating selectors that return objects with the same keys as the input selectors object, where each value is computed by the corresponding selector.
Creates a selector that returns an object with the same shape as the input selectors object, but with values computed by running each selector.
/**
* Creates a structured selector from an object of selectors
* @param selectors - Object where each property is a selector function
* @returns OutputSelector that returns object with same keys but computed values
*/
function createStructuredSelector<TSelectors extends SelectorsObject>(
selectors: TSelectors
): OutputSelector<
GetStateFromSelectors<TSelectors>,
SelectorResultsMap<TSelectors>
>;
/**
* Creates a structured selector with a custom selector creator
* @param selectors - Object where each property is a selector function
* @param selectorCreator - Custom createSelector function to use
* @returns OutputSelector using the custom selector creator
*/
function createStructuredSelector<TSelectors extends SelectorsObject>(
selectors: TSelectors,
selectorCreator: CreateSelectorFunction
): OutputSelector<
GetStateFromSelectors<TSelectors>,
SelectorResultsMap<TSelectors>
>;Basic Usage:
import { createStructuredSelector } from "reselect";
// Individual selectors
const selectItems = (state) => state.items;
const selectLoading = (state) => state.loading;
const selectError = (state) => state.error;
const selectUser = (state) => state.user;
// Create structured selector
const selectAppData = createStructuredSelector({
items: selectItems,
loading: selectLoading,
error: selectError,
user: selectUser
});
// Usage
const appData = selectAppData(state);
// Result: { items: [...], loading: false, error: null, user: {...} }With Custom Selector Creator:
import { createStructuredSelector, createSelectorCreator, lruMemoize } from "reselect";
// Custom selector creator with LRU memoization
const createLRUSelector = createSelectorCreator({
memoize: lruMemoize,
memoizeOptions: { maxSize: 10 }
});
// Structured selector using custom creator
const selectDashboardData = createStructuredSelector({
stats: selectStats,
recentActivity: selectRecentActivity,
notifications: selectNotifications
}, createLRUSelector);Nested Structured Selectors:
import { createStructuredSelector } from "reselect";
// Create nested structure
const selectUserProfile = createStructuredSelector({
personal: createStructuredSelector({
name: (state) => state.user.name,
email: (state) => state.user.email,
avatar: (state) => state.user.avatar
}),
preferences: createStructuredSelector({
theme: (state) => state.user.preferences.theme,
language: (state) => state.user.preferences.language,
notifications: (state) => state.user.preferences.notifications
}),
stats: createStructuredSelector({
loginCount: (state) => state.user.stats.loginCount,
lastLogin: (state) => state.user.stats.lastLogin
})
});
// Result has nested structure:
// {
// personal: { name: "...", email: "...", avatar: "..." },
// preferences: { theme: "...", language: "...", notifications: ... },
// stats: { loginCount: 42, lastLogin: "..." }
// }Creating selectors for each key in a root state object.
import { createStructuredSelector } from "reselect";
// For a state shape like: { todos: [...], users: [...], settings: {...} }
const selectAllSlices = createStructuredSelector({
todos: (state) => state.todos,
users: (state) => state.users,
settings: (state) => state.settings
});
// Equivalent to manually writing:
const selectAllSlicesManual = createSelector(
[(state) => state.todos, (state) => state.users, (state) => state.settings],
(todos, users, settings) => ({ todos, users, settings })
);interface RootState {
users: User[];
posts: Post[];
ui: {
loading: boolean;
error: string | null;
};
}
interface User {
id: number;
name: string;
email: string;
}
interface Post {
id: number;
title: string;
authorId: number;
}
// Typed structured selector
const selectPageData = createStructuredSelector({
users: (state: RootState) => state.users,
posts: (state: RootState) => state.posts,
loading: (state: RootState) => state.ui.loading,
error: (state: RootState) => state.ui.error
});
// TypeScript infers the return type:
// {
// users: User[];
// posts: Post[];
// loading: boolean;
// error: string | null;
// }Computed Properties in Structured Selectors:
import { createStructuredSelector, createSelector } from "reselect";
// Mix simple selectors with computed ones
const selectOrderSummary = createStructuredSelector({
// Simple selectors
items: (state) => state.cart.items,
customer: (state) => state.customer,
// Computed selectors
subtotal: createSelector(
[(state) => state.cart.items],
(items) => items.reduce((sum, item) => sum + item.price * item.quantity, 0)
),
tax: createSelector(
[(state) => state.cart.items, (state) => state.customer.taxRate],
(items, taxRate) => {
const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
return subtotal * taxRate;
}
),
total: createSelector(
[
(state) => state.cart.items,
(state) => state.customer.taxRate
],
(items, taxRate) => {
const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
const tax = subtotal * taxRate;
return subtotal + tax;
}
)
});Create pre-typed versions of createStructuredSelector with predefined state types.
import { createStructuredSelector } from "reselect";
interface RootState {
todos: { id: number; completed: boolean }[];
users: { id: number; name: string }[];
ui: { loading: boolean; error: string | null };
}
// Create a pre-typed createStructuredSelector
export const createStructuredAppSelector = createStructuredSelector.withTypes<RootState>();
// Now use it without specifying state types
const selectAppData = createStructuredAppSelector({
// Type of `state` is automatically set to `RootState`
todos: state => state.todos,
users: state => state.users,
loading: state => state.ui.loading,
error: state => state.ui.error
});
// TypeScript knows the return type:
// {
// todos: { id: number; completed: boolean }[];
// users: { id: number; name: string }[];
// loading: boolean;
// error: string | null;
// }type SelectorsObject = Record<string, Selector>;
type SelectorResultsMap<TObject extends SelectorsObject> = {
[Key in keyof TObject]: ReturnType<TObject[Key]>;
};
type RootStateSelectors<TState> = {
[Key in keyof TState]: Selector<TState, TState[Key]>;
};
type GetStateFromSelectors<TSelectors extends SelectorsObject> =
TSelectors[keyof TSelectors] extends Selector<infer State, any> ? State : never;
interface StructuredSelectorCreator {
<TSelectors extends SelectorsObject>(
selectors: TSelectors
): OutputSelector<GetStateFromSelectors<TSelectors>, SelectorResultsMap<TSelectors>>;
<TSelectors extends SelectorsObject>(
selectors: TSelectors,
selectorCreator: CreateSelectorFunction
): OutputSelector<GetStateFromSelectors<TSelectors>, SelectorResultsMap<TSelectors>>;
withTypes: <OverrideStateType>() => StructuredSelectorCreator<OverrideStateType>;
}
type TypedStructuredSelectorCreator<State> = <TSelectors extends RootStateSelectors<State>>(
selectors: TSelectors
) => OutputSelector<State, SelectorResultsMap<TSelectors>>;Install with Tessl CLI
npx tessl i tessl/npm-reselect