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

  1. Store (Almacén): Es un contenedor para el estado de la aplicación. Solo hay un único almacén en una aplicación NgRx.
  2. State (Estado): Representa el estado de la aplicación en un momento dado.
  3. 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.
  4. Reducers (Reductores): Son funciones puras que toman el estado actual y una acción, y devuelven un nuevo estado.
  5. Selectors (Selectores): Son funciones que seleccionan una parte del estado del almacén.
  6. 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:

npm install @ngrx/store @ngrx/effects @ngrx/store-devtools

Configuración del Almacén

  1. 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,
};
  1. 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');
  1. 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);
}
  1. 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:

  1. Definir el Estado Inicial:
// src/app/state/app.state.ts
export interface AppState {
  counter: number;
}

export const initialState: AppState = {
  counter: 0,
};
  1. 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');
  1. 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);
}
  1. 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 {}
  1. 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

Módulo 2: Conceptos Básicos de TypeScript

Módulo 3: Componentes y Plantillas

Módulo 4: Directivas y Pipes

Módulo 5: Servicios e Inyección de Dependencias

Módulo 6: Enrutamiento y Navegación

Módulo 7: Formularios en Angular

Módulo 8: Cliente HTTP y Observables

Módulo 9: Gestión de Estado

Módulo 10: Pruebas en Angular

Módulo 11: Temas Avanzados

Módulo 12: Despliegue y Mejores Prácticas

© Copyright 2024. Todos los derechos reservados