Cómo usar NgRx Selectors en Angular

En NgRx, cuando queremos obtener datos del store, la forma más rápida y directa suele ser usar store.select. Nos permite consultar cualquier porción (slice) del estado. Sí, suena curioso, pero cualquier porción nos devuelve un Observable<any>.
Por ejemplo:

Esto es flexible, pero también peligroso: ¿qué pasa si la estructura de nuestro state cambia? La mejor solución para evitar dolores de cabeza es usar Selectors. ¡Vamos a ver cómo funcionan!
NgRx Selectors
Los selectores nos ayudan a obtener partes del estado del store con total seguridad de tipos (type safety). Funcionan como un espejo de nuestro estado, lo que facilita consultarlo en cualquier parte y nos ayuda a no duplicar código en la aplicación.
NgRx nos ofrece dos funciones principales para crearlos: createFeatureSelector() y createSelector().
La función createFeatureSelector nos permite definir una porción tipada de nuestro estado a partir de su interfaz o tipo:
export const selectHomeState = createFeatureSelector<HomeState>('home');La función createSelector toma el featureState como primer parámetro y una función secundaria para leer ese estado y extraer la propiedad exacta que necesitamos:
export const selectLoading = createSelector(
selectHomeState,
(homeState) => homeState.loading
)Ahora que ya conocemos las dos funciones básicas de NgRx, vamos a ponerlas en práctica en un proyecto real 🔥.
Creando selectores
Es momento de usar createFeatureSelector y createSelector en nuestro proyecto. Vamos a continuar con el repositorio del artículo inicial sobre NgRx: clónalo y cámbiate a la rama action-creators:
git clone https://github.com/danywalls/start-with-ngrx.git
git switch action-creatorsAbre el proyecto en tu editor favorito y crea el archivo src/app/pages/about-me/state/home.selectors.ts. Ahora importa las funciones createFeatureSelector y createSelector. Usa createFeatureSelector junto con la interfaz HomeState para crear selectHomeState:
export const selectHomeState = createFeatureSelector<HomeState>('home');Después de eso, usa selectHomeState para crear los selectores de players, loading y acceptTerms:
export const selectLoading = createSelector(
selectHomeState,
(homeState) => homeState.loading
)
export const selectPlayers = createSelector(
selectHomeState,
(homeState) => homeState.players
)
export const selectAcceptTerms = createSelector(
selectHomeState,
(homeState) => homeState.acceptTerms,
)También podemos componer selectores. Por ejemplo, si queremos saber cuándo players tiene datos y el usuario ya aceptó los términos (acceptTerms), podemos crear selectAllTaskDone. Este selector combina selectPlayers y selectAcceptTerms para verificar que ambas condiciones se cumplan:
export const selectAllTaskDone = createSelector(
selectPlayers,
selectAcceptTerms,
(players, acceptTerms) => {
return acceptTerms && players.length > 0;
}
)El código completo de home.selectors.ts queda de la siguiente manera:
import {createFeatureSelector, createSelector} from "@ngrx/store";
import {HomeState} from "./home.state";
export const selectHomeState = createFeatureSelector<HomeState>('home');
export const selectLoading = createSelector(
selectHomeState,
(homeState) => homeState.loading
)
export const selectPlayers = createSelector(
selectHomeState,
(homeState) => homeState.players
)
export const selectAcceptTerms = createSelector(
selectHomeState,
(homeState) => homeState.acceptTerms,
)
export const selectAllTaskDone = createSelector(
selectPlayers,
selectAcceptTerms,
(players, acceptTerms) => {
return acceptTerms && players.length > 0;
}
)Muy bien, con los selectores listos, es momento de refactorizar home.component.ts para empezar a usarlos. Importa cada selector desde home.selectors.ts.
Nota: Elimina la función
toSignaly utilizastore.selectSignalpara transformar automáticamente los observables de los selectores en signals.
public $loading = this._store.selectSignal(selectLoading);
public $players = this._store.selectSignal(selectPlayers);
public $acceptTerms = this._store.selectSignal(selectAcceptTerms);Por último, crea una variable nueva para usar el selector compuesto selectAllTaskDone:
public $allTasksDone = this._store.selectSignal(selectAllTaskDone);Actualiza el template de home.component.html para usar la signal $allTasksDone:
@if (!$allTasksDone()) {
Wait for the players and accept terms
} @else {
<h2>Everything done!🥳</h2>
}Guarda los cambios y todo seguirá funcionando sin problemas 😄. Para probar los selectores compuestos: cuando se dispare playersLoadSuccess y hagas clic en acceptTerms, verás el mensaje "Everything done!".

Conclusión
En este artículo aprendimos cómo usar selectors para consultar y gestionar porciones de estado en NgRx en lugar de depender directamente de store.select. Con createFeatureSelector y createSelector ganamos tipado seguro, memoización y la capacidad de componer lógica reutilizable de forma limpia.
Modern frontend development — Angular, React, TypeScript, accessibility, and performance.
Preguntas Frecuentes
¿Cuál es la diferencia entre createFeatureSelector y createSelector en NgRx?
createFeatureSelector crea un selector tipado de nivel superior que apunta a una porción (feature slice) específica del estado global del store. createSelector toma uno o más de estos selectores como proyectores de entrada y deriva porciones más específicas a partir de ellos. Juntos te permiten componer selectores de forma mantenible y fácil de probar, sin dispersar el acceso directo al estado por todos tus componentes.
¿Por qué debería usar selectores de NgRx en lugar de acceder al estado directamente con store.select?
Usar store.select directamente con una ruta en string o una función anónima devuelve un Observable de tipo any, lo que elimina el tipado seguro y puede fallar silenciosamente si cambia la estructura del estado. Los selectores creados con createFeatureSelector y createSelector tienen tipado completo, cuentan con memoización para recalcularse solo cuando sus entradas cambian, y se pueden reutilizar en múltiples componentes, reduciendo la duplicación y mejorando el rendimiento.
¿Cómo funciona la memoización en los selectores de NgRx y por qué mejora el rendimiento?
Los selectores de NgRx tienen memoización por defecto, lo que significa que guardan en caché su último resultado calculado y solo vuelven a calcularlo cuando uno de sus selectores de entrada emite una nueva referencia. Esto evita cálculos innecesarios y previene disparar la detección de cambios en los componentes de Angular cuando el valor del estado no ha cambiado realmente, algo muy valioso en aplicaciones grandes con estado derivado complejo.
Artículos Relacionados
Cómo depurar NgRx usando Redux DevTools en Angular
Aprende a depurar el estado de NgRx en Angular moderno con Redux DevTools: configura provideStoreDevtools, rastrea acciones, inspecciona cambios y usa time-travel debugging.
Cómo manejar efectos secundarios en Angular usando NgRx Effects
Los efectos secundarios son habituales en nuestras aplicaciones. Aprende a manejarlos en Angular con NgRx Effects para evitar sobrecargar tus componentes y mantener tu estado limpio y predecible.
Cómo implementar ActionCreationGroup en NgRx
Cuando creas acciones en NgRx, normalmente usas la función auxiliar createAction. Escribimos los nombres de las acciones con un string directo sobre el origen y el evento, como [Cart] Update Price. Esta acción se vincula claramente con la funcionalidad Cart en nuestra aplicación...
Cómo entender y usar la función inject() en Angular
Guía práctica sobre la función inject() de Angular: inicializadores de propiedades, eliminación de código repetitivo en herencia, guards funcionales e InjectionContext.
Cómo probar componentes en Angular usando TestBed
Aprende a probar componentes en Angular con TestBed, manejar componentes hijos y mocks de servicios, y migrar de Karma/Jasmine al moderno Vitest con Standalone y Signals.
Compartir este artículo
Si esta guía te resultó útil, compártela con tu equipo o con otros desarrolladores.
Software real. Lecciones reales.
Comparto las lecciones que aprendí por las malas, para que puedas evitarlas o estar preparado cuando ocurran.
Únete a 13,800+ desarrolladores y lectores.
Cero spam. Cancela tu suscripción en cualquier momento.