Wann Angular begéint Redux

Préambule

Mir héieren ëmmer méi iwwer d'wuessend Notzung vum Redux Konzept mat Angular (2+). Wärend ech no verschiddenen Artikelen gesicht hunn fir mech zum Thema ze guidéieren, hunn ech gemierkt datt vill ze wéineg an eiser schéiner Molière Sprooch zougänglech sinn. Dësen Artikel zielt dofir Iech ze hëllefen Redux ze verstoen, a seng Notzung mat engem Kader deen net am Fong säi léifste Feld ass. Wann Dir Angular benotzt an net mat dësem Konzept vertraut sidd, sidd Dir op déi richteg Plaz komm.

Viraussetzunge

Dësen Artikel ass geduecht fir Entwéckler déi gewinnt sinn Angular ze benotzen ouni eng kloer Iddi ze hunn wat Redux ass. Konzepter wéi Observables, Basis Notioune vun Typescript a Komponente gi selbstverständlech geholl. Wëssen iwwer funktionell Programméierung ass e Plus.

Introduktioun

Redux ass eng Architektur déi zielt den Zoustand vun Ärer Applikatioun op enger eenzeger Plaz ze konzentréieren. Aktiounen ginn duerch Eventer geschéckt, déi all Kéier de fréiere Staat huelen, en Deel dovun änneren, an en neien zréckginn, dee benotzt gëtt fir Är Donnéeën ze managen.
Ass et net ganz kloer? Keng Suergen, d'Zil vun dësem Artikel ass e scheinbar komplizéiert Konzept ze demystifiéieren, awer dat ass einfach a logesch.
Ier ech zum Kär vun der Saach kommen, wëll ech d'Tatsaach ënnersträichen datt Redux, ganz dacks mat React benotzt, op kee Fall vun deem Leschten ofhängeg ass. Et ass héich Zäit datt mir d'Virdeeler vun enger ganz praktescher Architektur verstoen fir mat all komponentorientéierte Kader / Bibliothéik ze benotzen, an Angular ass e perfekt Beispill.

Wat sinn d'Problemer am Zesummenhang mat Angular déi duerch nei Applikatiounsdatenmanagement geléist kënne ginn?

Variablen gedeelt duerch verschidde Komponente vu verschiddene Niveauen

An enger Angular Komponent am Allgemengen ass et selten Variabelen an ze hunn readonly fir alles anescht wéi Konstanten oder Service Observables. D'Variabelen, déi direkt an der Komponent ugewise ginn, ginn och vun deemselwechte Komponent geännert. Awer wou mir e richtege Problem kënnen hunn ass wann dës Variablen als Input un eng Ënnerkomponent passéiert ginn, a si gi vun dëser Ënnerkomponent geännert. Et ass méiglech an et funktionnéiert well dem Angular seng Dateverbindung dofir gemaach ass. Wéi och ëmmer, et gëtt ganz schwéier et ze testen, an de Code kann Nebenwirkungen hunn.
Loosst eis eng Bamstruktur virstellen wéi:

Komponent0 ├── Komponent 1 └── Komponent 2 └── Komponent 3

Mir hunn eng Variabel, déi mir fir d'Komponenten 1 an 3 gemeinsam wëllen. Also, mir si verflicht dës Variabel als Eegeschafte vu Component0 ze deklaréieren, nëmmen fir datt se am Component2 souwéi am Component3 aktualiséiert gëtt. Mir mussen en Ausgang an de Komponent 1 setzen wa mir d'Variabel no engem Event änneren wëllen. A maachen déi selwecht, net nëmmen am Component3, awer och an Component2 fir zréck op Component0 mat EventEmitter.

Vill Code, fir einfach eng Variabel ze änneren déi op 2 Plazen op der selwechter Säit benotzt gëtt.

Natierlech huet Angular scho Léisungen déi an dësem Fall benotzt kënne ginn. D'Benotzung besonnesch vu Servicer, an deenen déi gemeinsam Variabel gespäichert wier. Wa mir wollten datt et a béid Komponenten gebonnen ass, dat heescht datt et an Echtzäit am Component1 ännert wann et am Component3 geännert gouf, da misste mir reaktiv Programméierungsparadigme benotzen, notamment Observables mat RXJS. Et ass machbar, a vill Uwendungen funktionnéieren op dës Manéier, awer et kann schwéier ginn ze erhalen wann d'Muster regelméisseg widderhëlt, op verschiddene Plazen an der Applikatioun. Dës Iddi dierf awer net ganz ausgeschloss ginn, well se eng Basis fir eis Léisung gëtt.

Binded Daten si ganz liichtflüchteg an Debugging ka komplizéiert sinn

Daten kënnen déi ganzen Zäit änneren, an ausser Dir schafft stänneg op eng onverännerbar Manéier, d'Verfollegung vun Ännerungen an de Wäerter vun engem Objet ka komplizéiert ginn. Et ass och schwéier eng Geschicht vun Aktiounen an Eventer ze halen, déi iwwer e bestëmmte Moment duerchgefouert goufen. Redux bitt och eng Léisung op dësem Niveau mat sengem "Time-travel Debugging". Redux hält eng Kopie vu senger viregter Versioun all Kéier wann en neie Staat erstallt gëtt, an alles ass verfügbar an Dev Tools iwwer d'Extensioun

Et kann schwéier sinn Code ze organiséieren déi Variabelen op eng pur Manéier ännert.

Hei kréie mir wierklech an en Deel vun der Erklärung vu funktionneller Programméierung, awer eng Léisung, déi vun dësem Paradigma gëtt, ass d'Konzept vun "reng Funktioun". Eng reng Funktioun huet een oder méi Argumenter, ännert keng Variabel ausserhalb sengem Ëmfang, a gëtt e Wäert zréck, deen ëmmer d'selwecht wäert sinn mat deeneselwechten Argumenter. Et ass ganz einfach an der Theorie, an net ëmmer an der Praxis.
Wann Dir wëllt méi déif an d'Erklärung an d'Verstoe vum funktionnelle Programméierungsparadigma tauchen, kann ech nëmmen en Artikel vun De Yoan Ribeiro zum Thema.
Ausserdeem féiert d'Benotzung vu Angular eis dacks OOP ze maachen anstatt funktionell Programméierung, am Sënn datt mir d'Donnéeën vun der Komponent änneren (wat eng Klass ass) déi per Definitioun extern sinn zu de Methoden vun der Komponent.

E bëssen Theorie virun der Praxis

Elo datt mir e puer Punkten inherent zur Entwécklung mat Angular opgeworf hunn an déi mat Redux vereinfacht kënne ginn, wäerte mir probéieren d'Muster gutt ze verstoen, andeems Dir eng Basis Redux Applikatioun architektéiert.

Dir wäert heiansdo Store gesinn, heiansdo Staat. Déi zwee Wierder decken allgemeng déiselwecht Bedeitung, nämlech de Staat vun der Uwendung a seng Datestruktur.

Dëst Diagramm ass ganz einfach. Vun eisem Komponent wäerte mir eng Aktioun verschéckt. Mir kënnen dëst duerch den einfachen Tatsaach iwwersetzen en Uruff ze schécken, en Event dat selwer eng Funktioun nennt.
Da gëtt dës Aktioun op eng applizéiert Reduzéierung déi wäert Akt op der Staat vun der Applikatioun. Et ass schlussendlech dee selwechte Staat dee vum Komponent (en) an der UI gelies gëtt.
An zwee Sätz an engem einfachen Diagramm konnte mir d'Konzept ganz einfach zesummefaassen. Awer offensichtlech, Aktiounen, Reduzéierer a Staat hunn all eng Roll. A wann et vläicht wéi vill Code a Funktiounen schénge fir e puer Saachen ze maachen, si jidderee grouss Wichtegkeet.
Wéi mir kënne gesinn, muss de Flux ëmmer sinn unidirektional, an dat ass e wichtege Punkt, deen als Grondsteen vun eiser Architektur wäert déngen. Benotzeraktioune ginn duerch d'Komponente déi Aktiounen verschéckt. Dës Aktiounen ginn un d'Reduktioune geschéckt, eis reng Funktiounen déi en neie Staat fir d'Applikatioun zréckginn.
Mir hu virdru geschwat iwwer eng Suerg fir Nebenwirkungen wann Dir Variabelen änneren, déi vu verschiddene Komponenten gedeelt ginn. Redux bitt eng richteg Léisung op dësem Niveau andeems Dir op d'Konzept vu funktionneller Programméierung fokusséiert, an net objektorientéiert. Minimaliséierung vun Nebenwirkungen, all Bestanddeel, egal wéi et ass, wäert nëmmen op de Staat handelen, deen dofir néideg ass. Mir wäerten eng Aktioun schécken fir eng Immobilie vum Staat ze aktualiséieren. Dann all Plaz wou dës Propriétéit als Verännerlechen an engem Komponent benotzt ginn, et gëtt gebonnen an dofir geännert, all an enger propper Aktioun déi nëmmen dës Propriétéit geännert hunn.

Beispill vun engem Reduzéierer

const reducer: Reducer<AppState> = (state: AppState, action: Action) => {
  switch (action.type) {
    case "IS_LOADING":
        return {
            ...state,
            isLoading: action.payload
        };
    default:
        return state;
  }
};

Zäit fir Praxis

Hei ass e bësse méi komplizéiert Diagramm deen e richtege Gebrauchsfall am Kontext vun der Entwécklung vun enger Applikatioun reflektéiert.

NDD: eng Notzlaascht ass den Numm deen un eng Variabel vun all Typ gëtt deen an der Handlung geschéckt gëtt a vum Reduzéierer benotzt gëtt.

Loosst eis e Fall huelen, wou mir eng Applikatioun bauen déi ausgesäit wéi e Blog, awer déi sech mat Manga beschäftegt (fir Artikelen z'änneren).
Mir wäerten e Komponent hunn deen d'Donnéeën vun engem Manga iwwer seng ID weist (präsent an der URL). Loosst eis virstellen datt den Titel vum Manga, säin Auteur a seng Zuel vu Bänn Daten sinn déi gelueden sinn wann d'Säit ugewise gëtt, awer net seng Beschreiwung. Dëst ass ganz laang, Dir musst op e Knäppchen klickt fir se ze weisen an dofir lueden.
Wann Dir op dëse Knäppchen klickt, wäerte verschidde Saache geschéien. Eis Komponent verschéckt eng Aktioun déi d'Beschreiwung vum Manga iwwer d'Funktioun lued loadMangaDescription(). Dës Funktioun wäert éischtens d'Aktioun verschéckt DESCRIPTION_IS_LOADING avec un Notzlaascht zu wouer. Mir wëllen de Benotzer weisen datt d'Informatioun gelueden gëtt. Zum Beispill, soulaang dëse Wäert richteg ass, kënne mir e Spinner weisen. De Komponent wäert also dëse Wäert vum Staat recuperéieren, dee vum Reduzéierer aktualiséiert gouf, deen d'Aktioun genannt huet DESCRIPTION_IS_LOADING.
Wann mir eise Staat komplett synchron aktualiséiert hunn fir ze soen datt d'Beschreiwung lued, kënne mir weidergoen. De Komponent huet net nëmmen dës Handlung geschéckt, awer et huet och e Service genannt deen d'Beschreiwung iwwer en HTTP-Uruff kritt. Wien seet HTTP hei seet asynchron, also wësse mir net wéini d'Äntwert kënnt. Dëst ass allgemeng firwat mir Spinner weisen, wéi an dësem Beispill.
Wann d'Ufro zréckkënnt, gëtt de Service d'Resultat op de Komponent zréck, deen endlech déi folgend zwou Aktiounen lancéiere kann (duerch d'Verbindung mam Resultat iwwer Verspriechen oder Abonnement). De Komponent wäert also fäeg sinn d'Aktioun nach eng Kéier ze verschécken DESCRIPTION_IS_LOADING mat dëser Zäit eng Notzlaascht um falsch.
Mä dat ass net alles. D'Zil vun eiser Operatioun, aus der Komponent, ass d'Beschreiwung vum Manga ze recuperéieren. Et ass gutt fir e Spinner beim Luede ze setzen an ze läschen wann d'Donnéeën opgeholl ginn, awer Dir musst nach ëmmer dës Donnéeën benotzen. Fir dëst wäerte mir eng zweet Handlung no der éischter schécken déi d'Luede op falsch setzt, wat wäert sinn LOAD_DESCRIPTION. Dës Aktioun enthält an der Notzlaascht d'Donnéeën déi mir un de Reduzéierer schécken wëllen an dann am Staat fannen. Et sinn dës Donnéeën déi dann op der UI ugewise ginn, dem Komponent geliwwert.
De Produit Code fir dëst Beispill kann op dëser fonnt ginn Github Repository e méi déif Verständnis ze hunn fir Redux iwwer Theorie an Diagrammer ze benotzen. Et ass relativ einfach a entsprécht engem NgRedux Startup, mat de meeschte wesentleche fir d'Architektur ze schaffen.

D'Epos

En anert Konzept bruecht vum Redux ass dat vun Epics. Am virege Fall, zum Beispill, wëlle mir net verpflichte ze spezifizéieren datt wann de Service Uruff gemaach ass an d'Aktioun verschéckt ass, mir wëllen och datt de Loader verschwannen wann Dir eng nei Aktioun verschéckt. Wa mir d'Aktioun musse verschécken, déi d'Donnéeën op e puer Plazen fëllt, wäerte mir dann gezwongen sinn de Code ze duplizéieren fir de Loader ze verschwannen. Dëst ass wou Epics an d'Spill kommen. En Epic ass eng Funktioun déi vun engem Reduzéierer ausgeléist gëtt a wäert e Stroum vun Aktiounen huelen fir en anere Stroum vun Aktiounen zréckzekommen. An eisem Fall wier et duer ze soen, datt all Kéier d'Aktioun LOAD_DESCRIPTION geschéckt gëtt, wëlle mir och d'Aktioun verschéckt DESCRIPTION_IS_LOADING mat der Notzlaascht op falsch gesat. Einfach an effizient, keng Code Duplikatioun oder zousätzlech Fäll ze managen.
Méi Informatioun iwwer d'Konzept vun Epics an hir Benotzungsfäll:
- https://medium.com/kevin-salters-blog/epic-middleware-in-redux-e4385b6ff7c6
- https://redux-observable.js.org/docs/basics/Epics.html

Angular-Redux

Bis dohinner bleift nach eng Fro. Mir wësse gutt wéi mir Eventer op der Komponent lauschteren fir eis Handlungen ze verschécken. Mir wësse wéi een e Service an et pluggt wa mir asynchron Donnéeën brauchen, a wéi mir eis Handlungen op d'Reduktiounen effizient verschéckt sou datt Datenännerungen am Staat net kollidéieren. Ausser datt wann mir eise Staat aktualiséiert hunn, ass de Wee fir d'Elementer ze recuperéieren nach e bëssen onkloer.
Wéi weess ech wann eng Variabel Wäert geännert huet? A wéi kënne mir dësen neie Wäert an eiser Komponent benotzen?
Et ass fir dës Zort vu Froen ze beäntweren déi Librairien gär hunn Angular-Redux. Dëst ass Middleware déi eis erlaabt Elementer vun eisem Staat statesch oder dynamesch ze handelen an ze recuperéieren. Hei ass e Beispill Code fir eis Manga Beschreiwung dynamesch ze recuperéieren.

import { select } from '@angular-redux/store';
import { Observable } from 'rxjs/Observable';
import { MangaState } from '../store/state';
@Component({
    selector: 'app-manga',
})
export class MangaComponent {
    @select(['manga']) readonly manga: Observable;
    constructor() {}
}

Aus dem Code aus dem Repository uewen geholl a vereinfacht, erlaabt dëse Code eis en Iwwerbléck iwwer d'Erhuelung vun eisen Donnéeën aus dem Geschäft ze hunn.
Dat éischt Schlësselwuert an der Linn ass @auswielen deen en Dekorateur ass, deen duerch Angular-Redux geliwwert gëtt an deen eis erlaabt eis Manga Element aus dem Buttek ze recuperéieren an en Observable ze maachen. Wat d'Aart vun eiser Variabel entsprécht, andeems d'MangaState Interface als Input entsprécht, déi dem Typ vun eisem Objet entsprécht.
Vun do aus wäert et einfach sinn d'Donnéeën an HTML ze weisen. Wéi och ëmmer, et gëtt eng kleng Ännerung ze maachen fir den Observable ze managen.

<div *ngIf="manga | async; let manga">
    Nom du manga : {{ manga.name }}
    Auteur du manga : {{ manga.author }}
    <div>LOADING DESCRIPTION</div>
    <div>{{ manga.description }}</div>
</div>

Wéi mir kënne gesinn, musse mir den Observable asynchron verwalten. Den Observable ass e Stroum dee fixe Wäerter zur Zäit T kann hunn, awer fir dës Wäerter am HTML ze weisen, musst Dir e Päif setzen | async déi automatesch de leschte Wäert recuperéieren. Zweet Remarque, hannert der Päif, kënne mir eng lokal Variabel erstellen fir net eng async Päif ze maachen fir all Wäert ze weisen (wat méiglech wier). Also de {{ manga.name }} wäert tatsächlech den Numm Eegeschafte vun der Manga lokal Variabel erstallt weisen, net déi vun der Observable.

Feedback

Nodeems ech u Angular Uwendungen vun Null mat an ouni Redux geschafft hunn, géif ech soen datt et e bëssen Aarbecht brauch fir Redux ze denken, awer dat geschitt zimlech séier. Wéi vun enger Objektsprooch op eng funktionell Sprooch ze plënneren, musst Dir gewinnt sinn anescht ze kodéieren. Awer d'Spill ass definitiv derwäert, an d'Kombinatioun vu Redux + Tippen + Testen ass e Gewënner Combo fir eng Applikatioun extrem zolidd, liicht debugged an maintenanceable. Natierlech dauert seng Ëmsetzung méi laang well e Store muss gutt duerchduecht ginn no de Bedierfnesser vun der Applikatioun. D'Trennung vun Daten tëscht verschiddene Reduzéierer kann och e Punkt sinn deen d'Ënnerhaltbarkeet bestëmmen ofhängeg vun der Wiederverwendung vun dëse Reduzéierer vu verschiddene Säiten. Redux ass eng oppe Architektur an där Dir souguer Mustere kënnt addéieren, déi Äre Bedierfnesser entspriechen, sou wéi d'Epics duerch en anere System vun Dispatcheren ersetzen, déi eleng Verantwortung fir d'Versendaktiounen zum Beispill hunn.

Conclusioun

Mir gesinn datt eng Architektur wéi Redux mat all Typ vun Applikatioun interface kann. Dir musst einfach d'Nëtzlechkeet vum Muster verstoen fir Daten ze managen an de Gesamtzoustand vun Ärer Uwendung.
Dir musst just iwwer d'Extras denken fir se an Angular z'integréieren, sou wéi Angular-Redux fir d'Verbindung tëscht Komponentvariablen an dem globale Staat ze managen.
D'Benotzung vum Typescript hëlt seng voll Bedeitung, besonnesch duerch d'Tippen vum Geschäft an d'Donnéeën déi et enthält, déi an de Reduzéierer souwéi Aktiounen fonnt kënne ginn, a souguer Observablen a Komponenten (kuckt de Repository Code). Mir hunn dofir Sécherheet am ganze Code geliwwert andeems Dir op tippt vermeiden Feeler sou vill wéi méiglech.
En anere wichtege Punkt: Redux ass keng Magie. Natierlech musst Dir realiséieren datt Redux näischt méi ass wéi eng Architektur déi op Är Applikatioun bäigefüügt gëtt, awer déi net magesch all d'Musterproblemer läscht déi Är Applikatioun scho kann hunn. Och wann d'Bibliothéik eng staark Léisung fir d'Gestioun vun Applikatiounsdaten ubitt, brauch hir Ëmsetzung nach ëmmer Zäit. Dir musst suergfälteg iwwer d'Datenstruktur a seng Normaliséierung nodenken, well dëst e wesentlechen Impakt op d'Architektur vun Aktiounen, Geschäfter a Reduzéierer hunn. Dir musst d'Zäit huelen fir dës Donnéeën ze tippen an ze testen, souwéi d'Funktiounen déi se änneren fir eng Applikatioun gesond, funktionell an ouni Iwwerraschungen ze halen.
Geschriwwen vun William Barranco