7.1 Introducción a la Gestión de Estado
La gestión de estado es un aspecto crítico en las aplicaciones Angular. Ayuda a mantener y gestionar la información y los datos de la aplicación de una manera predecible y escalable. En Angular, la gestión de estado se puede lograr a través de librerías como NgRx.
7.2 Integración de NgRx para la Gestión de Estado
NgRx es una librería popular que implementa la arquitectura de Redux para gestionar el estado en aplicaciones Angular. Para integrar NgRx en tu proyecto, sigue estos pasos:
Instala NgRx:
npm install @ngrx/store @ngrx/effects @ngrx/entity @ngrx/router-store @ngrx/store-devtools --save
Configura el Store:
En tu módulo principal (generalmente, app.module.ts
), configura el store de NgRx. Define tus reducers y efectos, y proporciona el store en tu aplicación.
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
@NgModule({
imports: [
StoreModule.forRoot({ /* tus reducers aquí */ }),
EffectsModule.forRoot([/* tus efectos aquí */]),
]
})
export class AppModule { }
7.3 Creación de Acciones, Reducers y Efectos
- Acciones: Las acciones son objetos que describen un cambio en el estado de la aplicación. Debes definir tipos de acciones y crear funciones que retornen instancias de acciones.
- Reducers: Los reducers son funciones puras que toman el estado actual y una acción, y devuelven un nuevo estado. Debes definir reducers para manejar cada tipo de acción.
- Efectos: Los efectos son utilizados para gestionar efectos secundarios, como solicitudes HTTP. Debes definir efectos para escuchar acciones y realizar tareas asincrónicas.
7.4 Uso del Store en la Aplicación
Una vez que hayas configurado NgRx y definido acciones, reducers y efectos, puedes usar el store en tus componentes para acceder y modificar el estado de la aplicación.
- Selector de Estado: Puedes crear selectores para acceder a porciones específicas del estado de la aplicación. Los selectores ayudan a evitar la manipulación directa del estado.
- Dispatch de Acciones: Utiliza el método
dispatch
del store para disparar acciones en respuesta a eventos en tus componentes. - Observación del Estado: Utiliza observables para observar los cambios en el estado y actualizar la interfaz de usuario en consecuencia.
- Modificación del Estado: Si es necesario, puedes modificar el estado de la aplicación utilizando acciones y reducers, asegurándote de mantener un flujo de datos unidireccional.
Aquí tienes un ejemplo de código que muestra todos los conceptos sobre la gestión de estado con NgRx en Angular. En este ejemplo, crearemos una pequeña aplicación para gestionar una lista de tareas.
Paso 1: Configuración de NgRx
Instala NgRx: Ejecuta el siguiente comando para instalar las dependencias necesarias:
npm install @ngrx/store @ngrx/effects @ngrx/entity @ngrx/router-store @ngrx/store-devtools --save
Paso 2: Define las acciones, reducers y efectos
// Paso 2: Define las acciones
import { createAction, props } from '@ngrx/store';
export const addTask = createAction('[Task] Add Task', props<{ task: string }>());
export const completeTask = createAction('[Task] Complete Task', props<{ taskId: number }>());
export const deleteTask = createAction('[Task] Delete Task', props<{ taskId: number }>());
export const loadTasks = createAction('[Task] Load Tasks', props<{ tasks: string[] }>());
Paso 3: Define el estado y los reducers
// Paso 3: Define el estado y los reducers
import { createEntityAdapter, EntityState } from '@ngrx/entity';
import { createReducer, on } from '@ngrx/store';
import * as TaskActions from './task.actions';
export interface TaskState extends EntityState<string> {
selectedTaskId: number | null;
}
export const taskAdapter = createEntityAdapter<string>();
export const initialState: TaskState = taskAdapter.getInitialState({
selectedTaskId: null,
});
export const taskReducer = createReducer(
initialState,
on(TaskActions.addTask, (state, { task }) => taskAdapter.addOne(task, state)),
on(TaskActions.completeTask, (state, { taskId }) => taskAdapter.updateOne({ id: taskId, changes: { completed: true } }, state)),
on(TaskActions.deleteTask, (state, { taskId }) => taskAdapter.removeOne(taskId, state)),
on(TaskActions.loadTasks, (state, { tasks }) => taskAdapter.setAll(tasks, state))
);
Paso 4: Define los efectos
// Paso 4: Define los efectos
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { Injectable } from '@angular/core';
import { of } from 'rxjs';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { TaskService } from './task.service'; //
Asumimos que existe un servicio TaskService para cargar tareas
@Injectable()
export class TaskEffects {
loadTasks$ = createEffect(() =>
this.actions$.pipe(
ofType(TaskActions.loadTasks),
mergeMap(() =>
this.taskService.getTasks().pipe(
map((tasks) => TaskActions.loadTasks({ tasks })),
catchError(() => of(TaskActions.loadTasksFailure()))
)
)
)
);
constructor(private actions$: Actions, private taskService: TaskService) {}
}
Paso 5: Configurar el Módulo Principal
// Paso 5: Configurar el módulo principal
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { taskReducer } from './state/task.reducer';
import { TaskEffects } from './state/task.effects';
@NgModule({
imports: [
StoreModule.forRoot({ tasks: taskReducer }),
EffectsModule.forRoot([TaskEffects]),
],
})
export class AppModule {}
Paso 6: Uso del Store en un Componente
// Paso 6: Uso del Store en un Componente
import { Component, OnInit } from '@angular/core';
import { Store, select } from '@ngrx/store';
import { addTask, completeTask, deleteTask, loadTasks } from './state/task.actions';
import { TaskState } from './state/task.reducer';
import { selectAllTasks, selectSelectedTask } from './state/task.selectors';
@Component({
selector: 'app-task-list',
template: `
<ul>
<li *ngFor="let task of tasks$ | async">
{{ task }}
<button (click)="completeTask(task)"></button>
<button (click)="deleteTask(task)"></button>
</li>
</ul>
`,
})
export class TaskListComponent implements OnInit {
tasks$ = this.store.pipe(select(selectAllTasks));
selectedTask$ = this.store.pipe(select(selectSelectedTask));
constructor(private store: Store<TaskState>) {}
ngOnInit() {
this.store.dispatch(loadTasks());
}
addTask(task: string) {
this.store.dispatch(addTask({ task }));
}
completeTask(taskId: number) {
this.store.dispatch(completeTask({ taskId }));
}
deleteTask(taskId: number) {
this.store.dispatch(deleteTask({ taskId }));
}
}
Este ejemplo muestra cómo utilizar NgRx para gestionar el estado en una aplicación Angular. Puedes observar la definición de acciones, reducers, efectos, el uso del store en el componente y cómo las acciones se disparan en respuesta a eventos del usuario.
La gestión de estado con NgRx permite mantener una aplicación predecible y escalable, especialmente en aplicaciones con una gran cantidad de datos y complejidad. También facilita la depuración y el mantenimiento a medida que tu aplicación crece.