Uso de Redux con Angular

How to Implement NGRX State Management in Angular Web Application

Índice

Hoy vamos a aprender a introducir en el uso de REDUX gestión de estados en una aplicación web Angular con la biblioteca NGRX

El equivalente de REDUX para Angular es NGRX.

También le puede interesar: «Unimedia nombrada líder tecnológico por Clutch»

Además, nuestro equipo de desarrolladores es experto en crear software a medida según los requisitos del proyecto.

Veremos paso a paso cada uno de los componentes básicos de NGRX

Diagrama del ciclo de vida de la gestión de estados de NgRx
Fuente: https://ngrx.io/

Tienda

The Store es un contenedor de estado controlado diseñado para ayudar a escribir aplicaciones escalables y de alto rendimiento sobre Angular.

Efectos

Los Efectos utilizan los flujos para proporcionar nuevas fuentes de acciones. Modificar el estado en función de interacciones externas como solicitudes de red, mensajes de socket web y eventos temporales. Por lo tanto, son el método preferido para obtener datos y mantener parte de la lógica de la aplicación.

Selectores

Los selectores son funciones puras que se utilizan para obtener porciones del estado de la tienda. Así, seleccionan piezas de información del Almacén para que sean consumidas por otras partes de la lógica de la aplicación.

Reductores

Los Reductores en NgRx son responsables de manejar las transiciones de un estado al siguiente estado en tu aplicación. Las funciones reductoras gestionan estas transiciones determinando qué acciones gestionar en función del tipo de acción. En otras palabras, reciben acciones con una carga de datos y cambian el estado almacenando los nuevos datos según se desee.

Después de entender los bloques de construcción básicos de NGRX State Management, ahora vamos a construir una pequeña aplicación de chat de ejemplo con NGRX State Management.

Dado que, ya hemos creado una aplicación de chat antes En el post anterior aquí . Como resultado, vamos a introducir la gestión de estado NGRX en él para la actualización de la interfaz de usuario, mientras que el envío / recepción del mensaje.

https://www.unimedia.tech/2020/11/27/aws-amplify-with-angular-chat/

1. Configurar el proyecto

/** Clone Repo */
git clone https://github.com/unimedia-technology/amplify-chat-angular.git

/** Enter into project directory */
cd amplify-chat-angular

/** Install the dependencies */
npm i

/** Create a new git branch */
git checkout ngrx

2. Instalar las dependencias de NGRX

ng add @ngrx/store @ngrx/effects @ngrx/component

3. Crear acciones

Aquí, vamos a crear 4 acciones que son necesarias para gestionar el estado de la aplicación de chat.

Archivo: store/actions/actions.ts

import { createAction, props } from '@ngrx/store';

export const loadMessages = createAction('[Chat] Load Messages', props<{ channelId: string }>());
export const loadMessagesSuccess = createAction('[Chat] Load Messages Success', props<{ messages: any[] }>());

export const sendMessage = createAction('[Chat] Send Message', props<{ message }>());
export const addMessageToList = createAction('[Chat] Add Message To List', props<{ message }>());

4. Crear efectos

Los efectos proporcionan una forma de interactuar con esos servicios y aislarlos de los componentes.

Utilización

Los efectos son útiles, Si desea manejar tareas como la obtención de datos, tareas de larga duración que producen múltiples eventos, y otras interacciones externas.

Nota: Para recordar, en la mayoría de los escenarios, enviará acción(es), pero no es obligatorio enviar siempre acción(es).

Para escribir efectos sin enviar la(s) acción(es), pase el segundo parámetro a las funciones createEffect() con { dispatch: false }

Archivo: state/effects/effects.ts

import { Actions, createEffect, ofType } from '@ngrx/effects';
import { Injectable } from '@angular/core';
import { map, switchMap } from 'rxjs/operators';
import * as chatActions from '../actions/actions';
import { from } from 'rxjs';
import { APIService } from '../../API.service';

@Injectable()
export class ChatEffects {

  constructor(
    private actions$: Actions,
    private api: APIService,
  ) { }

  /** Load the List of Messages */
  loadMessages$ = createEffect(() => this.actions$.pipe(
    ofType(chatActions.loadMessages),
    switchMap(({ channelId }) => {
      return from(this.api.MessagesByChannelId(channelId)).pipe(
        map((res: any) => {
          return chatActions.loadMessagesSuccess({ messages: res.items });
        }),
      );
    })
  ));

  /** Send message and call no actions */
  sendMessage$ = createEffect(() => this.actions$.pipe(
    ofType(chatActions.sendMessage),
    switchMap(({ message }) => {
      return from(this.api.CreateMessage(message));
    })
  ), { dispatch: false });
}

5. Crear reductores

Cada función reductora toma el último Action enviado, el estado actual, y determina si devuelve un estado recién modificado o el estado original. Ahora, en el siguiente ejemplo te guiaremos sobre cómo escribir funciones reductoras, registrarlas en tu Store, y componer estados de funciones.

Archivo: store/reducers/reducers.ts

import { Action, createReducer, on } from '@ngrx/store';
import * as chatActions from '../actions/actions';

export interface IChatState {
  messages: any[];
}

/** Initial State */
export const initialState: IChatState = {
  messages: [],
};

export function chatReducer(state: IChatState | undefined, action: Action): IChatState {
  return reducer(state, action);
}

const reducer = createReducer<IChatState>(
  initialState,

  /** Loaded Messafes */
  on(chatActions.loadMessagesSuccess, (state, { messages }) => ({
    ...state,
    messages
  })),

  /** Add message to the messages array */
  on(chatActions.addMessageToList, (state, { message }) => ({
    ...state,
    messages: [...state.messages, message]
  })),
);

6. Crear selectores

El selector que hemos creado aquí devolverá el observable de todos los mensajes

Archivo: store/selectors/selectors.ts

import { createSelector } from '@ngrx/store';

export const selectChatState = (state) => state;

export const selectMessages = createSelector(
  selectChatState,
  (state) => state.chat.messages
);

7. Gestionar el Estado

En esta sección, vamos a ver cuales son todos los eventos donde necesitamos actualizar el estado:

  • Cargar la aplicación
  • Enviar un mensaje
  • Recibir un mensaje

Archivo: app.component.ts

import { Component, OnInit } from '@angular/core';
import { NavigationEnd, Router, RouterEvent } from '@angular/router';
import { select, Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { delay } from 'rxjs/operators';
import { APIService } from './API.service';
import { addMessageToList, loadMessages, sendMessage } from './store/actions/actions';
import { IChatState } from './store/reducers/reducer';
import { selectMessages } from './store/selectors/selectors';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  title = 'amplify-chat-angular';
  username: string;
  messages: Observable<any[]>;

  constructor(
    private api: APIService,
    private router: Router,
    private store: Store<IChatState>
  ) { }

  ngOnInit(): void {
    this.router.events.subscribe((events: RouterEvent) => {
      if (events instanceof NavigationEnd) {
        const qParams = this.router.routerState.snapshot.root.queryParams;
        if (qParams && qParams.user) {
          this.username = qParams.user;
        } else {
          this.router.navigate(['/'], { queryParams: { user: 'Dave' } });
        }
      }
    });

    this.listMessages();
    this.onCreateMessage();
  }

  send(event, inputElement: HTMLInputElement): void {
    event.preventDefault();
    event.stopPropagation();
    const input = {
      channelID: '2',
      author: this.username.trim(),
      body: inputElement.value.trim()
    };

    this.store.dispatch(sendMessage({ message: input }));
    inputElement.value = '';
  }

  listMessages(): void {
    this.store.dispatch(loadMessages({ channelId: '2' }));
    this.messages = this.store.pipe(
      select(selectMessages),
      delay(10)
    );
  }

  onCreateMessage(): void {
    this.api.OnCreateMessageListener.subscribe(
      {
        next: (val: any) => {
          console.log(val);
          this.store.dispatch(addMessageToList({ message: val.value.data.onCreateMessage }));
        }
      }
    );
  }
}

Explicación

  • listMessages envía la acción loadMessages con channelId para recuperar todos los mensajes.
  • Cuando el usuario envía un mensaje, se llama a la acción sendMessage, que envía el mensaje.
  • Cuando el usuario recibe un mensaje, se envía la acción addMessageToList y se añade el mensaje a la lista messages.

8. Crear plantilla

En esta sección, vamos a utilizar, ngrxPush tubería de @ngrx/component.

El tubo ngrxPush sirve como sustituto del tubo async.

ngrxPush contiene un manejo inteligente de la detección de cambios que nos permitirá funcionar tanto en modo de zona llena como de zona vacía sin ningún cambio en el código.

Uso:

La tubería ngrxPush se suministra a través de ReactiveComponentModule. Por lo tanto, para utilizarlo, añada el ReactiveComponentModule a la dirección imports de su NgModule.

Archivo: app.component.html

<div id="root">
  <div class="container">
    <div class="messages">
      <div class="messages-scroller">
        <ng-container *ngIf="messages$ | async as messages">
          <ng-container *ngFor="let message of messages">
            <div [ngClass]="message.author === username ? 'message me' : 'message'">
              {{message.body}}
            </div>
          </ng-container>
        </ng-container>
      </div>

    </div>
    <div class="chat-bar">
      <div class="form">
        <input #messageInput type="text" name="messageBody" placeholder="Type your message here" value="
          (keyup.enter)="send($event, messageInput)" />
      </div>
    </div>
  </div>
</div>

Eso es todo, ahora estamos gestionando el estado utilizando NGRX.

¿Te ha gustado leer esto y te gustaría aprender más sobre angular y redux?, aquí tienes otro gran artículo, ¡échale un vistazo!

Unimedia Technology

Aquí en Unimedia Technology tenemos un equipo de desarrolladores Angular que pueden desarrollar sus más desafiantes Web Dashboards y Web apps.

Recuerde que en Unimedia somos expertos en tecnologías emergentes, así que no dude en ponerse en contacto con nosotros si necesita asesoramiento o servicios. Estaremos encantados de ayudarle.

Unimedia Technology

Su socio de desarrollo de software

Somos una consultora tecnológica de vanguardia especializada en arquitectura y desarrollo de software a medida.

Nuestros servicios

Suscríbase a nuestras actualizaciones

Mantente al día, informado y ¡demos forma juntos al futuro de la tecnología!

Lecturas relacionadas

Profundice con estos artículos

Descubra más opiniones expertas y análisis en profundidad de Unimedia en el ámbito del desarrollo de software y la tecnología.

Let’s make your vision a reality!

Simply fill out this form to begin your journey towards innovation and efficiency.

Hagamos realidad tu visión.

Sólo tienes que rellenar este formulario para iniciar tu viaje hacia la innovación y la eficiencia.