createSelector

A selector that derives a value — a total, a filtered list, a sorted view — returns a new array or object on every action, defeating useSelector's reference check. createSelector, re-exported by Redux Toolkit 3,824 from Reselect 19,013 , memoizes it: input selectors extract the raw pieces, and the result function runs only when one of those pieces changes identity.

Memoized derivation, countedJavaScript
import { createSelector } from '@reduxjs/toolkit';
const state = { lines: [{ name: 'Mug', price: 9 }, { name: 'Cap', price: 14 }], vat: 0.5 };
let runs = 0;
const selectLines = s => s.lines, selectVat = s => s.vat;   // cheap, no allocation
const selectTotal = createSelector([selectLines, selectVat], (lines, vat) => {
  runs += 1;                                   // count recomputations
  return lines.reduce((t, l) => t + l.price, 0) * (1 + vat);
});
console.log(selectTotal(state), selectTotal(state), 'result fn ran', runs, 'time(s)');
console.log(selectTotal({ ...state, other: 1 }), 'still', runs);   // inputs unchanged
console.log(selectTotal({ ...state, vat: 0 }), 'now', runs);       // one input changed
Output
34.5 34.5 result fn ran 1 time(s)
34.5 still 1
23 now 2

The second line is the point: a brand-new root state object did not trigger a recomputation, because state.lines and state.vat were still the same references. Keep input selectors dumb — property access only, never .map or an object literal — or every call produces fresh inputs and nothing is ever cached.

Reselect 5, inside Redux Toolkit 2, changed the default memoizer from lruMemoize, whose cache held one result, to weakMapMemoize, which keys results on argument identity. That killed the classic bug where two components calling selectItemById(state, 1) and selectItemById(state, 2) in alternation recomputed on every render, so the old selector-factory-per-component fix is obsolete.