All topics
Stateadvanced

NgRx Selectors

Explain createSelector's memoization and composition benefits over reading state directly from the store.

Selectors are the recommended, and effectively mandatory-in-practice, way to read state out of an NgRx store — rather than components reaching directly into the raw state shape (store.select(state => state.products.items)), createSelector builds composable, memoized query functions that decouple components from the store's internal state structure and avoid redundant recomputation of derived values.

It's like a company's official reporting dashboard that pulls from raw underlying databases through a defined, stable API — analysts consuming the dashboard never need to know if the underlying database schema changes, and the dashboard only recalculates its summary figures when the relevant raw numbers actually change, not on every single page refresh.

Key Concepts

1
createSelector accepts one or more "input selectors" (simpler selectors, often reading a single slice of state) and a "projector" function that combines their results — and critically, this projector is memoized: it only re-runs if at least one of the input selectors' results has changed since the last call, using the same reference-equality-based memoization principle that makes computed() signals efficient, applied here to derived state queries instead.
createSelectorcomputed()
2
This composability is the real payoff at scale: a selectVisibleProducts selector can be built from a selectAllProducts selector and a selectSearchTerm selector, and if either of those lower-level selectors is later reused elsewhere or their underlying state shape changes, only the input selectors need updating — the composed selector's projector logic (the actual filtering logic) stays untouched as long as its inputs' shapes remain the same.
selectVisibleProductsselectAllProductsselectSearchTerm
3
An interview-relevant nuance is that selectors decouple components from the store's internal shape entirely: a component only ever imports and uses selectVisibleProducts, never needing to know or care whether products lives directly on the root state or nested three levels deep inside a feature slice — this indirection is what makes large-scale refactors of the underlying state shape possible without touching every component that reads from it, since only the selector definitions themselves need to change.
selectVisibleProductsproducts