All questions

Reselect Selectors

Premium

Reselect Selectors

A memoized selector derives a value from state and recomputes only when the specific slices of state it reads change by reference. This is the core idea behind reselect, the standard memoization layer for Redux selectors: createSelector takes any number of cheap input selectors plus one result function, and it reruns the result function only when one of the input values changes — otherwise it hands back the previously cached result. createStructuredSelector composes a map of selectors into one selector that yields an object of their results, memoized the same way.

Build reselectSelectors, an object exposing createSelector and createStructuredSelector, plus a recomputations() counter on every selector so you can prove the cache is being reused.

Signature

type Selector = ((state: any, ...args: any[]) => any) & {
  recomputations(): number;
};

const reselectSelectors: {
  // (...inputSelectors, resultFn) -> a memoized selector
  createSelector(...args: Function[]): Selector;

  // { key: selector, ... } -> a selector returning { key: result, ... }
  createStructuredSelector(map: Record<string, Function>): Selector;
};

Examples

const selectTotal = reselectSelectors.createSelector(
  (state) => state.cart.items,
  (items) => items.reduce((sum, item) => sum + item.price, 0),
);

const state = { cart: { items: [{ price: 10 }, { price: 5 }] } };
selectTotal(state); // 15 — runs the result function
selectTotal(state); // 15 — same items reference, returns the cached 15
selectTotal.recomputations(); // 1
const selectView = reselectSelectors.createStructuredSelector({
  total: (state) => state.total,
  user: (state) => state.user,
});

const r1 = selectView({ total: 42, user: { name: 'Ada' } });
// { total: 42, user: { name: 'Ada' } }

selectView.recomputations(); // 1

Notes

  • Input selectors always run — they are cheap reads of state; only the result function is memoized behind the reference check.
  • Reference equality — input values are compared with ===. Immutable updates give a changed slice a new reference; an unchanged slice keeps its old one.
  • Cache size is 1 — only the last inputs and last result are remembered, so alternating between two states recomputes each time.
  • recomputations() — returns how many times the result function ran, so a test can assert the cache was reused across calls.
  • Do not worry about — custom equality functions, more than one cache entry, or memoizing the input selectors themselves.

FAQ

Why compare inputs by reference instead of deep equality?
Redux state is immutable, so a real change always produces a new reference. A reference check is O(1) and correct, while a deep comparison walks the whole input on every call and usually costs more than the derivation it is trying to skip.
What does recomputations() tell you?
It returns how many times the result function actually ran. In tests it proves the cache was reused: the count stays flat across calls whose inputs did not change and increments only on a real change.
Why does the cache only hold one entry?
createSelector remembers just the last inputs and last result, so calling it alternately with two different states recomputes every time. Reselect keeps cache size 1 by default because a selector is usually read with one state per render.

Unlock the solution & editor

  • Runnable editor + tests

    Solve in the browser with instant Jest feedback.

  • Detailed solutions

    Walkthroughs, edge cases, and complexity notes.

  • Multi-framework variants

    React, Vue, Vanilla, Angular — same question, different stacks.

Upgrade to Premium