Fundamental Elements of NgRx: Store, Actions, Reducers, Selectors, Effects

The store is the key element in the whole state management process. Se säilyttää tilaa ja helpottaa komponenttien ja tilan välistä vuorovaikutusta. Voit saada viittauksen storeen Angular-riippuvuusinjektiolla, kuten alla on esitetty.

constructor(private store: Store<AppState>) {}

Tätä store-viittausta voidaan myöhemmin käyttää kahteen ensisijaiseen toimintoon:

  • Toimintojen lähettämiseen varastoon store.dispatch(…)-metodin kautta, jotka puolestaan käynnistävät reduktorit ja efektit
  • Sovelluksen tilan hakemiseen selektoreiden kautta

Tilaobjektipuun rakenne

Esitettäköön, että sovelluksesi koostuu kahdesta ominaisuusmoduulista nimeltä Käyttäjä (User) ja Tuote (Product). Kumpikin näistä moduuleista käsittelee eri osia kokonaistilasta. Tuotetietoja ylläpidetään aina tilan products-osiossa. Käyttäjätietoja ylläpidetään aina tilan user-osiossa. Näitä osioita kutsutaan myös viipaleiksi.

Actionit

Action on käsky, jonka lähetät varastoon, valinnaisesti jonkinlaisen metatiedon (hyötykuorman) kanssa. Kauppa päättää action-tyypin perusteella, mitä operaatioita se suorittaa. Koodissa toiminto esitetään tavallisena JavaScript-oliona, jolla on kaksi pääattribuuttia, nimittäin type ja payload. payload on valinnainen attribuutti, jota vähentäjät käyttävät tilan muuttamiseen. Seuraava koodinpätkä ja kuva havainnollistavat tätä käsitettä.

{ "type": "Login Action", "payload": { userProfile: user }}

NgRx:n versiossa 8 on apufunktio nimeltä createAction, jonka avulla voidaan määritellä action-luojat (ei siis actionit vaan action-luojat). Seuraavassa on esimerkkikoodi tätä varten.

Voit sitten käyttää login action creator -funktiota (joka on funktio) actionien rakentamiseen ja niiden lähettämiseen varastoon alla esitetyllä tavalla. user on payload-objekti, jonka välität actioniin.

this.store.dispatch(login({user}));

Reduktiot

Reduktiot vastaavat tilan muokkaamisesta ja palauttavat uuden tila-objektin muutoksineen. Redusoijat ottavat vastaan kaksi parametria, nykyisen tilan ja toiminnon. Vastaanotetun toimintotyypin perusteella redusoijat suorittavat tiettyjä muutoksia nykyiseen tilaan ja tuottavat uuden tilan. Tätä konseptia esitellään alla olevassa kaaviossa.

Actionien tapaan NgRx tarjoaa apufunktion nimeltä createReducer reduktoreiden luomista varten. Tyypillinen createReducer-funktion kutsu olisi seuraavanlainen.

Kuten näet, se ottaa sisäänsä alkutilan (tila sovelluksen käynnistyessä) ja yhdestä tilasta useampaan -tilanmuutosfunktiot, jotka määrittelevät, miten reagoidaan erilaisiin toimiin. Jokainen näistä tilanmuutosfunktioista vastaanottaa parametrina nykyisen tilan ja toiminnon ja palauttaa uuden tilan.

Efektit

Efektien avulla voit suorittaa sivutehosteita, kun toiminto lähetetään varastoon. Yritetään ymmärtää tätä esimerkin avulla. Kun käyttäjä kirjautuu onnistuneesti sovellukseen, toiminto type Login Action lähetetään varastoon, jossa on payload käyttäjän tiedot. Reducer-funktio kuuntelee tätä toimintoa ja muuttaa tilaa käyttäjätiedoilla. Lisäksi sivutoimena halutaan tallentaa käyttäjätiedot myös selaimen paikalliseen tallennustilaan. Efektiä voidaan käyttää tämän lisätehtävän (sivuvaikutuksen) suorittamiseen.

NgRx:ssä on useita tapoja luoda efektejä. Seuraavassa on raaka ja itsestään selvä tapa luoda efektejä. Huomaa, että tätä tapaa ei yleensä käytetä efektien luomiseen. Otin tämän vain esimerkkinä selittääkseni, mitä verhon takana tapahtuu.

  • actions$ observable emittoi myymälän vastaanottamat toimenpiteet. Nämä arvot käyvät läpi operaattoriketjun.
  • ofType on ensimmäinen käytetty operaattori. Tämä on NgRx:n (Not RxJS) tarjoama erikoisoperaattori, joka suodattaa actionit niiden tyypin perusteella. Tässä tapauksessa vain login-tyyppiset toiminnot pääsevät operaattoriketjun muiden osien läpi.
  • tap on ketjun toinen operaattori, jota käytetään käyttäjätietojen tallentamiseen selaimen paikalliseen tallennustilaan. tap-operaattoria käytetään yleensä operaattoriketjussa sivutehosteiden suorittamiseen.
  • Viimeiseksi meidän on tilattava manuaalisesti login$-havainnoitava.

Tällä lähestymistavalla on kuitenkin pari merkittävää haittapuolta.

  • Havainnoitava on tilattava manuaalisesti, mikä ei ole hyvä käytäntö. Näin joudut aina poistamaan tilauksen manuaalisesti, mikä johtaa ylläpidettävyyden puutteeseen.
  • Jos operaattoriketjussa ilmenee virhe, observable tekee virheen ja lopettaa seuraavien arvojen (toimintojen) lähettämisen. Tämän seurauksena sivuvaikutusta ei suoriteta. Siksi on oltava käytössä mekanismi, jolla voidaan manuaalisesti luoda uusi observable-instanssi ja kirjata uudelleen, jos virhe tapahtuu.

Tämän ongelman voittamiseksi NgRx tarjoaa createEffect-nimisen apufunktion efektien luomista varten. Tyypillinen createEffect-funktion kutsu näyttää seuraavalta.

Metodi createEffect ottaa sisäänsä funktion, joka palauttaa parametreina observable-olion ja (valinnaisesti) konfiguraatio-olion.

NgRx hoitaa tukifunktion palauttaman observable-olion tilaamisen, joten sinun ei tarvitse manuaalisesti tilata tai peruuttaa tilausta. Lisäksi jos operaattoriketjussa tapahtuu jokin virhe, NgRx luo uuden observable-olion ja tilaa sen uudelleen varmistaakseen, että sivuvaikutus suoritetaan aina.

Jos konfiguraatioobjektissa dispatch on true (oletusarvo), createEffect-metodi palauttaa Observable<Action>. Muussa tapauksessa se palauttaa Observable<Unknown>. Jos dispatch-ominaisuus on true, NgRx tilaa palautetun havainnoitavan type Observable<Action> ja lähettää vastaanotetut toiminnot varastoon.

Jos vastaanotettua toimintoa ei kuvata operaattoriketjussa erityyppiseen toimintoon, sinun on asetettava dispatch arvoksi false. Muuten suoritus johtaa äärettömään silmukkaan, koska sama toiminto lähetetään ja vastaanotetaan actions$-virtaan uudelleen ja uudelleen. Esimerkiksi alla olevassa koodissa ei tarvitse asettaa dispatch:n arvoksi false, koska alkuperäinen toiminto liitetään operaattoriketjussa eri tyyppiseen toimintoon.

Yllä olevassa skenaariossa

  • Efekti vastaanottaa toimintoja type loadAllCourses.
  • Asiointirajapintaa kutsutaan ja sivutehosteena ladataan kursseja.
  • Asiointirajapinnan vastaus toimintoon type allCoursesLoaded kartoitetaan ja ladatut kurssit välitetään payload:nä toimintoon.
  • Ja lopuksi luotu allCoursesLoaded-toiminto lähetetään varastoon. Tämän tekee NgRx konepellin alla.
  • Reduktori kuuntelee saapuvaa allCoursesLoaded-toimintoa ja muokkaa tilaa ladatuilla kursseilla.

Selektiot

Selektiot ovat puhtaita funktioita, joita käytetään viipaleiden saamiseen kaupan tilasta. Kuten alla näkyy, voit kysyä tilaa myös ilman selektoreiden käyttöä. Mutta tässäkin lähestymistavassa on pari merkittävää haittapuolta.

const isLoggedIn$ = this.store.pipe(map(state => !!state.user));
  • store on observable, jonka voit tilata. Aina kun kauppa vastaanottaa toiminnon, store työntää tila-objektin eteenpäin tilaajilleen.
  • Voit käyttää kartoitusfunktioita saadaksesi viipaleita tilasta ja suorittaaksesi tarvittaessa laskentaa. Yllä olevassa esimerkissä hankimme user-viipaleen tila-objektipuusta ja muunnamme sen booleaniksi määrittääksemme, onko käyttäjä kirjautunut sisään vai ei.
  • Voit joko tilata manuaalisesti isLoggedIn$-havainnoitavan isLoggedIn$-objektipuun tai käyttää sitä Angular-templaattorissa asynkronisella putkella lukeaksesi emittoituja arvoja.

Tämässä toimintatavassa on kuitenkin suuri haittapuoli. Yleensä kauppaan tulee usein toimintoja sovelluksen eri osista. Edellä esitetyn toteutuksen mukaan joka kerta, kun myymälä vastaanottaa toiminnon, myymälä emittoi tila-objektin. Ja tämä tila-objekti käy jälleen läpi kartoitusfunktion ja päivittää käyttöliittymän.

Jos kuitenkin kartoitusfunktion tulos ei ole muuttunut edellisestä kerrasta, käyttöliittymää ei tarvitse päivittää uudelleen. Jos esimerkiksi map(state => !!state.user):n tulos ei ole muuttunut edellisestä suorituksesta, meidän ei tarvitse työntää tulosta uudelleen UI:lle/Tilaajalle. Tämän saavuttamiseksi NgRx (ei RxJS) on ottanut käyttöön erityisen operaattorin nimeltä select. select-operaattorin avulla yllä oleva koodi muuttuu seuraavasti:

const isLoggedIn$ = this.store.pipe(select(state => !!state.user));

select-operaattori estää arvojen työntämisen UI:lle/tilaajille, jos kartoitusfunktion tulos ei ole muuttunut edellisestä kerrasta.

Tätä lähestymistapaa voidaan vielä parantaa. Vaikka select-operaattori ei työntäisikään muuttumattomia arvoja UI:lle/tilaajille, sen on silti joka kerta otettava tila-objekti ja tehtävä laskutoimitus tuloksen johtamiseksi.

Kuten jo edellä selitettiin, observable lähettää state:n, kun myymälä vastaanottaa toiminnon sovellukselta. Toiminto ei aina päivitä tilaa. Jos tila ei ole muuttunut, ei myöskään kartoitusfunktion laskennan tulos muutu. Siksi meidän ei tarvitse tehdä laskentaa uudelleen, jos emittoitu state-objekti ei ole muuttunut edellisestä kerrasta. Tässä kohtaa selektorit astuvat kuvaan.

Selektori on puhdas funktio, joka ylläpitää muistia edellisistä suorituksista. Niin kauan kuin syöte ei ole muuttunut, tulostetta ei lasketa uudelleen. Sen sijaan tuotos palautetaan muistista. Tätä prosessia kutsutaan memoisaatioksi.

NgRx tarjoaa apufunktion nimeltä createSelector memoisaatiokykyisten selektorien rakentamiseen. Seuraavassa on esimerkki createSelector-hyödykefunktiosta.

Funktio createSelector ottaa sisäänsä yhdestä moniin kartoitusfunktioita, jotka antavat erilaisia viipaleita tilasta, ja projektorifunktion, joka suorittaa laskennan. Projektorifunktiota ei kutsuta, jos tilaviipaleet eivät ole muuttuneet edellisestä suorituksesta. Jotta voit käyttää luotua valitsijafunktiota, sinun on annettava se argumenttina select-operaattorille.

this.isLoggedIn$ = this.store .pipe( select(isLoggedIn) );

Vastaa

Sähköpostiosoitettasi ei julkaista.