CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-reselect

Selectors for Redux - memoized functions for computing derived data from state.

Pending
Overview
Eval results
Files

structured-selectors.mddocs/

Structured Selectors

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.

Capabilities

createStructuredSelector

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: "..." }
// }

Root State Selectors

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 })
);

TypeScript Usage with Typed State

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;
// }

Advanced Patterns

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;
    }
  )
});

Pre-Typed Structured Selectors

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;
// }

Types

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

docs

development.md

index.md

memoization.md

selector-creation.md

selector-creator.md

structured-selectors.md

tile.json