NgRx Store es una biblioteca para la gestión de estado en aplicaciones Angular. Se basa en el patrón Redux, que proporciona un enfoque predecible para gestionar el estado de la aplicación mediante un único almacén (store) y flujos de datos unidireccionales. En esta lección, aprenderemos los conceptos básicos de NgRx Store, cómo configurarlo en una aplicación Angular y cómo utilizarlo para gestionar el estado de la aplicación.
Conceptos Clave de NgRx Store
- Store (Almacén): Es un contenedor para el estado de la aplicación. Solo hay un único almacén en una aplicación NgRx.
- State (Estado): Representa el estado de la aplicación en un momento dado.
- Actions (Acciones): Son eventos que describen algo que ha sucedido en la aplicación. Las acciones son el único medio para enviar datos al almacén.
- Reducers (Reductores): Son funciones puras que toman el estado actual y una acción, y devuelven un nuevo estado.
- Selectors (Selectores): Son funciones que seleccionan una parte del estado del almacén.
- Effects (Efectos): Son utilizados para manejar operaciones asíncronas y otras tareas secundarias.
Configuración de NgRx Store
Instalación
Para comenzar a usar NgRx Store, primero debemos instalar las dependencias necesarias:
Configuración del Almacén
- Definir el Estado Inicial: Creamos una interfaz para definir la estructura del estado y un objeto para el estado inicial.
// src/app/state/app.state.ts export interface AppState { counter: number; } export const initialState: AppState = { counter: 0, };
- Crear Acciones: Definimos las acciones que pueden modificar el estado.
// src/app/state/counter.actions.ts import { createAction } from '@ngrx/store'; export const increment = createAction('[Counter] Increment'); export const decrement = createAction('[Counter] Decrement'); export const reset = createAction('[Counter] Reset');
- Crear Reductores: Definimos los reductores que especifican cómo el estado cambia en respuesta a las acciones.
// src/app/state/counter.reducer.ts import { createReducer, on } from '@ngrx/store'; import { increment, decrement, reset } from './counter.actions'; import { AppState, initialState } from './app.state'; const _counterReducer = createReducer( initialState, on(increment, (state) => ({ ...state, counter: state.counter + 1 })), on(decrement, (state) => ({ ...state, counter: state.counter - 1 })), on(reset, (state) => ({ ...state, counter: 0 })) ); export function counterReducer(state: AppState | undefined, action: Action) { return _counterReducer(state, action); }
- Registrar el Almacén en el Módulo Angular: Configuramos el almacén en el módulo principal de la aplicación.
// src/app/app.module.ts import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { StoreModule } from '@ngrx/store'; import { counterReducer } from './state/counter.reducer'; @NgModule({ declarations: [AppComponent], imports: [ BrowserModule, StoreModule.forRoot({ counter: counterReducer }), ], providers: [], bootstrap: [AppComponent], }) export class AppModule {}
Uso de NgRx Store en Componentes
Seleccionar Estado
Para acceder al estado del almacén en un componente, utilizamos selectores.
// src/app/counter/counter.component.ts import { Component } from '@angular/core'; import { Store, select } from '@ngrx/store'; import { Observable } from 'rxjs'; import { AppState } from '../state/app.state'; @Component({ selector: 'app-counter', template: ` <div> <h1>{{ counter$ | async }}</h1> <button (click)="increment()">Increment</button> <button (click)="decrement()">Decrement</button> <button (click)="reset()">Reset</button> </div> `, }) export class CounterComponent { counter$: Observable<number>; constructor(private store: Store<AppState>) { this.counter$ = store.pipe(select('counter')); } increment() { this.store.dispatch(increment()); } decrement() { this.store.dispatch(decrement()); } reset() { this.store.dispatch(reset()); } }
Ejercicio Práctico
Ejercicio: Implementa una funcionalidad de contador utilizando NgRx Store. El contador debe poder incrementarse, decrementarse y reiniciarse a cero.
Solución:
- Definir el Estado Inicial:
// src/app/state/app.state.ts export interface AppState { counter: number; } export const initialState: AppState = { counter: 0, };
- Crear Acciones:
// src/app/state/counter.actions.ts import { createAction } from '@ngrx/store'; export const increment = createAction('[Counter] Increment'); export const decrement = createAction('[Counter] Decrement'); export const reset = createAction('[Counter] Reset');
- Crear Reductores:
// src/app/state/counter.reducer.ts import { createReducer, on } from '@ngrx/store'; import { increment, decrement, reset } from './counter.actions'; import { AppState, initialState } from './app.state'; const _counterReducer = createReducer( initialState, on(increment, (state) => ({ ...state, counter: state.counter + 1 })), on(decrement, (state) => ({ ...state, counter: state.counter - 1 })), on(reset, (state) => ({ ...state, counter: 0 })) ); export function counterReducer(state: AppState | undefined, action: Action) { return _counterReducer(state, action); }
- Registrar el Almacén en el Módulo Angular:
// src/app/app.module.ts import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { StoreModule } from '@ngrx/store'; import { counterReducer } from './state/counter.reducer'; @NgModule({ declarations: [AppComponent], imports: [ BrowserModule, StoreModule.forRoot({ counter: counterReducer }), ], providers: [], bootstrap: [AppComponent], }) export class AppModule {}
- Crear el Componente del Contador:
// src/app/counter/counter.component.ts import { Component } from '@angular/core'; import { Store, select } from '@ngrx/store'; import { Observable } from 'rxjs'; import { AppState } from '../state/app.state'; import { increment, decrement, reset } from '../state/counter.actions'; @Component({ selector: 'app-counter', template: ` <div> <h1>{{ counter$ | async }}</h1> <button (click)="increment()">Increment</button> <button (click)="decrement()">Decrement</button> <button (click)="reset()">Reset</button> </div> `, }) export class CounterComponent { counter$: Observable<number>; constructor(private store: Store<AppState>) { this.counter$ = store.pipe(select('counter')); } increment() { this.store.dispatch(increment()); } decrement() { this.store.dispatch(decrement()); } reset() { this.store.dispatch(reset()); } }
Conclusión
En esta lección, hemos aprendido los conceptos básicos de NgRx Store y cómo configurarlo en una aplicación Angular. Hemos visto cómo definir el estado inicial, crear acciones y reductores, y cómo utilizar el almacén en un componente. NgRx Store es una herramienta poderosa para gestionar el estado de aplicaciones Angular de manera predecible y escalable. En la próxima lección, exploraremos NgRx Effects para manejar operaciones asíncronas y otras tareas secundarias.
Curso de Angular 2+
Módulo 1: Introducción a Angular
- ¿Qué es Angular?
- Configuración del Entorno de Desarrollo
- Tu Primera Aplicación Angular
- Arquitectura de Angular
Módulo 2: Conceptos Básicos de TypeScript
- Introducción a TypeScript
- Variables y Tipos de Datos en TypeScript
- Funciones y Funciones Flecha
- Clases e Interfaces
Módulo 3: Componentes y Plantillas
- Creación de Componentes
- Plantillas de Componentes
- Estilos de Componentes
- Interacción entre Componentes
Módulo 4: Directivas y Pipes
Módulo 5: Servicios e Inyección de Dependencias
- Introducción a los Servicios
- Creación y Uso de Servicios
- Inyección de Dependencias
- Inyectores Jerárquicos
Módulo 6: Enrutamiento y Navegación
Módulo 7: Formularios en Angular
- Formularios Basados en Plantillas
- Formularios Reactivos
- Validación de Formularios
- Formularios Dinámicos
Módulo 8: Cliente HTTP y Observables
- Introducción al Cliente HTTP
- Realizando Solicitudes HTTP
- Manejo de Respuestas HTTP
- Uso de Observables
Módulo 9: Gestión de Estado
- Introducción a la Gestión de Estado
- Uso de Servicios para la Gestión de Estado
- NgRx Store
- NgRx Effects