Uso de Redux con Angular

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

UnimediaActualizado 9 de enero de 20246 min de lectura

Rótulo sobre la gestión de estado con NgRx en una aplicación web de Angular —store, acciones, efectos y reducers— con su logotipo

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 Storees un contenedor de estado controlado diseñado para ayudar a escribir aplicaciones escalables y de alto rendimiento sobre Angular.

Efectos

Los Efectosutilizan 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é accionesgestionar 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 efectosproporcionan 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 observablede 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.

ngrxPushcontiene 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 ReactiveComponentModulea 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.

Seguir leyendo

Más sobre Angular

Reserva una consulta gratuita

Cargando el calendario…

¿No carga el calendario? Ábrelo en una pestaña nueva, o llámanos al +34 936 01 40 40.