Foloseste cineva NgRx si de ce?

Salut. Lucrez cu Angular de 3 ani, dar de 2 luni am inceput sa folosesc Ngrx/Store, dupa ce am terminat un curs la o companie. In ultimele 3 zile am zis sa testez noile cunostinte si am creat un proiect nou, in care sa implementez NgRx pentru partea de autentificare (login, register, forgot-password). De acolo m-am intins putin si am extins si cu un user-profile pentru a avea un minim de rutare (urmeaza refactorizare cu 2 store-uri separate, AuthStore si UserStore). Pentru cine vrea sa arunce o privire: Github.

actiuni, reduceri, selectori, efecte, servicii. Toate cele 5 componente fac parte din ciclul unui store. Dezvoltarea este foarte lenta, liniile de cod se multiplica de vreo 5 ori. Debug si testare, nici nu-mi pot imagina (inca) cum se fac.

Pentru cele cateva actiuni ce fac parte din auth (login, register, logout, forgot-password, get-me, update-user, update-password) folosind NgRx am scris asa:

actions - 200 linii cod
effects - 260 linii cod
reducer - 340 linii cod
selectors - 8 linii cod
total: ~ 800 linii cod doar partea de autentificare si user profile. Cateva ore bune pierdute in plus pentru asta.
Pe langa liniile de cod, mai sunt si librariile de importat in proiect, ngrx/store, effects, entity.
nota: partea de Entity si selectorii nu cred ca au cea mai corecta implementare, dar nu asta e subiectul.

In trecut am lucrat la un erp in care store-ul era format din servicii si Observabile/Subjecturi, mult mai putin cod scris, mult mai clar, mult mai usor de intretinut, simplu. Componentele se inregistreaza la actiunile din store, iar cand una emite, celelalte asculta.

Nu cred ca exista un anume tip de aplicatie (mica/mare) pentru care sa se foloseasca sau nu NgRx, si pentru asta am gasit un articol care se potriveste fix cu parerea mea.

Daca, chiar este nevoie de salvarea state-ului aplicatiei, cred ca se poate face mult mai usor cu Observabile, iar NgRx-ul este doar un trend pentru ca in descrierea jobului apare tot mai des cerinta de a stii sa lucrezi cu aceasta librarie. O alta impresie a mea… e faptul ca moda asta vine mai mult din React, unde nu cred ca exista proiect fara Redux.

ps: categoria de frontend/js ar putea avea si subcategorii pentru cele mai folosite frameworkuri. cred ca ar rasari mai multe discutii pe tema asta

E o intrebare dificila, eu am trecut de la folosit ngrx pentru tot la a folosi doar in anumite locuri.

De exemplu partea de login propriu-zisa nu foloseste deloc ngrx dar pe urma ce actiuni se executa prima oara dupa login foloseste ngrx, la fel si la logout aceasi logica.
La o pagina de listare/detail view generica nu folosesc dar in schimb la o functionalitate de messagerie am folosit (sunt multe actiuni care au loc in fundal).

Ca si concluzie am folosit in general ngrx cand am avut nevoie de:

  • actiuni care se executa in fundal, interactiuni cu server (push)
  • elemente care apar pe mai multe pagini (anunturi favorite de exemplu) si atunci sa fie sincronizate
  • izolare de cod/functionalitate specifica (mobile device api calls)

Am evitat sa folosesc store-ul pentru managementul de entitati si formulare, nu mi sa parut un pattern placut.

eu credeam ca fix pentru liste si entitati e folosit :)) sa stochezi stateul fiecarui http request mi se pare abuziv si inutil (cam ce am facut eu in exemplul ala cu AuthStore). Voiam sa fac ceva interesant pentru “forgot-password”, am salvat in state fiecare email introdus + count pentru cate incercari sunt pe fiecare adresa, cu gandul ca la 5 incercari sa blochez accesul userului la aceasta actiune. Era mai ok de implementat pe login failed, dar store-ul nu e persistent si la refresh’uirea paginii, se duce naibi :)) (asta e functionalitate de backend mai mult cu afisare in frontend)

Din “auzite”, este foarte folosit in aplicatiile financiare, unde se lucreaza cu documente, rapoarte, liste mari. Aduci o lista de 1000 itemuri in store, afisezi prima pagina cu 100, cand treci pe pagina 2, le aduci instant din acesta. Mi se pare o implementare buna acolo unde interactiunea cu actiunile altor useri este scazuta, daca ai 2 operatori care lucreaza pe o lista, salvarea datelor intr-un store e perfecta. Daca ai 100 care tot interactioneaza cu lista respectiva, si datele sufera modificari destul de mult, atunci storeul ala devine outdated foarte rapid si tot ai nevoie de un nou request.

altceva ce mi se pare straniu … pentru o aplicatie care contine doar un sistem de autentificare, bundle-ul a depasit deja 2mb, nu dau vina neaparat pe librariile de ngrx si extra liniile de cod datorat store-ului, dar pare dubios tare :))

in josul articolului de pe medium pe care l-am pus in prima postare sunt 3 videoclipuri in care explica cum sa folosesti Rxjs si Subject’urile pentru a’ti crea propriul store, imi place mult mai mult decat ce ofera NgRx :grin: dar nu se vinde la fel de bine :))

Nu am experienta cu NgRx dar am putina cu VueX si de obicei sunt folosite pentru ca state management este greu in aplicatii async.

Poti implementa ceva gen store persist/restore care sa refaca starea store-ului la browser refresh dar mie nu mi-a placut modelul asta.

Problema mare care aduce store-ul este ca ai state-ul aplicatiei distribuit in doua locuri. Sa zicem ca vorbim de o adresa asta poate fi modificata in backend in timp ce in frontend pe store ea a fost cache-uita cu valoarea veche. Fara o integrare cu server push care sa invalideze datele de pe frontend e cam aiurea.

Chiar acuma incep un proiect nou, unific doua aplicatii web folosing web components cu stenciljs + ionic unde o sa definesc store-ul propriu cu rxjs si subjects, sper ca o sa mearga la fel de bine.

Redux are un dezavantaj major si anume boilerplate-ul…

Eu folosesc https://redux-toolkit.js.org cu createReducer, createAction si thunks, ceea ce reduce drastic boilerplate-ul dar chiar si asa o sa ai de scris teste in plus.

Pe react redux nu e recomandat in ziua de azi, doar ca orice proiect “enterprise” o sa necesite redux ca asa ne-am obisnuit. Azi poti folosi context, clientul apollo-graphql, recoiljs sau xstate in react daca vrei ceva fain, chiar si redux dupa cum am zis se foloseste cu redux toolkit.

Eu folosesc NgRx de aproximativ 1 an si sincer nu-mi mai imaginez viata fara, dar este vorba despre un proiect complex (views in views in views, internal tabs in tabs, etc.), unde folosesc @ngrx/entity si sunt multumit.
Dureaza putin sa inveti despre “good action hygiene” si de ce este foarte important cum iti definesti actiunile. Hint: e bine sa-ti modelezi actiunile ca events care exista in mod natural in domeniul aplicatiei tale. Actiunile trebuie descoperite, nu inventate artificial, altfel vei avea probleme mai tarziu.

Nu sunt expert in RxJs, dar pana nu am inceput sa folosesc NgRx, nu pot sa spun ca aplicatia mea era cu adevarat reactiva si predictibila. Acum, folosind si ChangeDetectionStrategy.OnPush peste tot, pot spune ca este si am ajuns sa implementez niste “refresh-uri” sau “sync-uri” care pareau extrem de complicate, intr-un mod foarte simplu doar dand dispatch la niste actiuni care existau deja.

O alta idee: multe lucruri pot fi facute cu selectori in loc de reduceri, asa ca mare atentie ce pui in State si foloseste selectorii cu incredere :slight_smile:. State-ul trebuie sa fie ca o baza de date normalizata, iar selectorii sunt queries care tranforma datele in orice forma ai nevoie de ele in componente. Astfel selectorii devin un fel de ViewModels si poti sa reduci destul de mult codul scris in componente.

Un alt sfat: foloseste modul mai nou de a scrie actions, reducers si effects, adica creator functions (createAction, createReducer, createEffect), mai reduce din boilerplate.

Ai pomenit ceva de servicii. Ce servicii? Singurele servicii pe care le mai am de cand folosesc NgRx sunt data services care aduc date de la API. In rest toata logica aplicatiei s-a mutat in reducers, selectors si effects, plus pure-functions pentru lucruri mai complicate.

sincer, gagisem libraria asta, dar cand am vazut ca are 100/200 downloads pe saptamana, am zis mai bine nu. ma gandesc ca o sa ajung sa depind de o librarie care poate ajunge inactiva foarte usor. In plus, nu stiu daca face ceva wow, altceva decat sa salveze niste obiecte tot prin localStorage/cookies, ceea ce as putea sa fac eu direct printr-un StorageService.

Pentru asta, ca rezolvare am vazut ca se foloseste versionare pe entitati. La fiecare modificare, se incrementeaza versiunea.

multumesc, foarte buna comparatia. Din ce am vazut, efectele sunt folosite pentru a apela serviciile (cele care fac api call-uri si trimite datele in reducer, pe langa asta putand executa alte “efecte” secundare, afisarea unui mesaj/popup de exemplu. Iar componentele raman destul de curate, pentru ca datele sunt deja pregatite de selectori in forma dorita.

pusesem pe lista si serviciile pentru ca de ele depind efectele.

haha, mi-am adus aminte :smiley: aseara am vrut sa implementez un sidebar, dar aveam deja un header. Am inclus headerul in sidebar, dar butonul de toggle sidebar este in header (proasta implementare, se putea face mult mai usor).
Ce am facut? comunicare intre sidebar si header prin EventEmitteri si Output.

Ce a trebuit pentru asta? in header declararea emiterului, o functie care sa emita valoarea (true/false), iar in sidenav … doar atribuirea unei metode pe acel emiter <app-header (onToggle)="onToggle($event)"></app-header>

Pentru un caz fix ca asta creasem topicul :crazy_face: as putea crea un MenuStore (si probabil asta o sa fac daca apar alte functionalitati), dar as fi scris de 10 ori mai mult cod. actions, reducers, effects, selectors, 4 fisiere in plus. Aici e un caz simplu pentru ca, comunicarea se face intr-o singura directie, dinspre header spre sidenav.

Deci, daca folosim ngrx si store’uri, facem store pentru fiecare modul (cu toate actiunile lui in parte), sau doar pentru cele carora vrem sa le pastram starea (in cazul asta, nu devine putin mai grea mentenanta aplicatiei? adica, ai o actiune, te astepti ca stateul ei sa fie in store, dar nu e, pentru ca nu aveai nevoie la un moment dat. sau invers, ai o actiune, verifici peste tot crezand ca nu e salvata, dar cand colo ii gaseai rezolvarea foarte usor in store)? sunt niste exemple cam trase de par, dar daca vine cineva nou pe proiect, probabil va acea ceva dificultati in a gasi problemele.

Daca folosesti ngrx nu inseamna ca trebuie sa il folosesti peste tot. Il folosesti acolo unde are sens si unde ai interactiuni mai complexe intre componente. Si eu am unele componente care apeleaza direct repositories si randeaza simplu, fara ngrx.

Exemplele tale sunt relativ simple, dar gandeste-te la niste scenarii unde ai zeci de componente (sau sute) in tree-ul de componente si trebuie sa comunici intre doua componente care au diferenta de cateva nivele intre ele. Cum faci?

  • Fara ngrx trebuie sa faci “bubble-up” la EventEmitters pana iti vine acru si iti garantez ca in scurt timp n-o sa mai intelegi cine, cui si cand paseaza datele.
  • Cu ngrx efectiv nu te mai intereseaza pe ce nivel sunt componentele, obtii decuplarea componentelor. Unele componente vor fi “smart” si vor face dispatch la actiuni, iar altele vor fi “dumb”, vor primi date pe Inputs si vor face randarea.

Nu stiu altii, dar eu intotdeauna am avut o multime de probleme cu “timingul” evenimentelor in aplicatiile web. Uneori o componenta se incarca/randa prea repede sau prea tarziu, iar alte componente dadeau erori din cauza asta. De cand folosesc ngrx si de cand am inceput sa invat despre reactive web apps, pot sa spun ca nu mai am nicio problema de acest fel. Efectiv nu ma mai intereseaza CAND vin datele, pentru ca aplicatia mea este reactiva si predictibila. Stiu ca oricand o componenta va primi date noi, ea va reactiona corespunzator si se va re-randa. Asta tine mai mult de ChangeDetectionStrategy.OnPush, dar merge mana-n mana cu ngrx si immutable data.

Probabil cineva care lucreaza cu React de multi ani rade de noi in acest moment :joy:. Din cate stiu eu, React face asta de la inceput, lui Angular i-a luat ceva timp si o rescriere completa sa ajunga aici.

1 Like

Problema nu e a la salvarea obiectelor ci atunci cand ajungi sa faci modificari la structura acestora. Sa zicem ai un obiect de tip user cu proprietati si faci o modificare la proprietatie acestui obiect vei ajunge sa faci caching la obiecte care au fost schimbate intre timp ca structura. La redux pe partea asta de persist exista si optiunea de a migra datele la fel ca la un DB, cand am vazut chestia asta am zis ca no way:

La mine strategia e ca fac caching/persistenta la client doar la elemente gen cheie valoare si restul se reconstruieste de la zero in store la pornirea aplicatiei. In cazul de mai sus, stochez doar cheia de autentificare si pe urma la pornire fac caching in store la ultima varianta o obiectului user.

1 Like