You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/translations/ro/7-bank-project/4-state-management
Lee Stott 2daab5271b
Update Quiz Link
3 weeks ago
..
README.md Update Quiz Link 3 weeks ago
assignment.md 🌐 Update translations via Co-op Translator 3 weeks ago

README.md

Construirea unei aplicații bancare Partea 4: Concepte de gestionare a stării

Chestionar înainte de curs

Chestionar înainte de curs

Introducere

Pe măsură ce o aplicație web crește, devine o provocare să urmărești toate fluxurile de date. Ce cod preia datele, ce pagină le consumă, unde și când trebuie actualizate... este ușor să ajungi la un cod dezordonat, greu de întreținut. Acest lucru este valabil mai ales atunci când trebuie să partajezi date între diferite pagini ale aplicației tale, de exemplu datele utilizatorului. Conceptul de gestionare a stării a existat întotdeauna în toate tipurile de programe, dar pe măsură ce aplicațiile web devin din ce în ce mai complexe, acesta a devenit un punct cheie de luat în considerare în timpul dezvoltării.

În această ultimă parte, vom analiza aplicația pe care am construit-o pentru a regândi modul în care este gestionată starea, permițând suportul pentru reîmprospătarea browserului în orice moment și păstrarea datelor între sesiunile utilizatorului.

Prerechizite

Trebuie să fi finalizat partea de preluare a datelor a aplicației web pentru această lecție. De asemenea, trebuie să instalezi Node.js și să rulezi serverul API local pentru a putea gestiona datele contului.

Poți testa dacă serverul funcționează corect executând această comandă într-un terminal:

curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result

Regândirea gestionării stării

În lecția anterioară, am introdus un concept de bază al stării în aplicația noastră cu variabila globală account, care conține datele bancare ale utilizatorului conectat în prezent. Totuși, implementarea noastră actuală are câteva defecte. Încearcă să reîmprospătezi pagina când ești pe tabloul de bord. Ce se întâmplă?

Există 3 probleme cu codul actual:

  • Starea nu este păstrată, deoarece o reîmprospătare a browserului te duce înapoi la pagina de autentificare.
  • Există mai multe funcții care modifică starea. Pe măsură ce aplicația crește, poate deveni dificil să urmărești modificările și este ușor să uiți să actualizezi una.
  • Starea nu este curățată, astfel încât atunci când faci clic pe Logout, datele contului sunt încă acolo, chiar dacă ești pe pagina de autentificare.

Am putea actualiza codul nostru pentru a aborda aceste probleme una câte una, dar acest lucru ar crea mai multă duplicare a codului și ar face aplicația mai complexă și mai greu de întreținut. Sau am putea să ne oprim câteva minute și să ne regândim strategia.

Ce probleme încercăm cu adevărat să rezolvăm aici?

Gestionarea stării se referă la găsirea unei abordări bune pentru a rezolva aceste două probleme particulare:

  • Cum să păstrăm fluxurile de date dintr-o aplicație ușor de înțeles?
  • Cum să menținem datele stării întotdeauna sincronizate cu interfața utilizatorului (și viceversa)?

Odată ce ai rezolvat aceste probleme, orice alte probleme pe care le-ai putea avea fie sunt deja rezolvate, fie au devenit mai ușor de rezolvat. Există multe abordări posibile pentru rezolvarea acestor probleme, dar vom merge pe o soluție comună care constă în centralizarea datelor și a modurilor de a le modifica. Fluxurile de date ar arăta astfel:

Schema care arată fluxurile de date între HTML, acțiunile utilizatorului și stare

Nu vom acoperi aici partea în care datele declanșează automat actualizarea vizualizării, deoarece este legată de concepte mai avansate de Programare Reactivă. Este un subiect bun de aprofundat dacă ești interesat.

Există multe biblioteci cu abordări diferite pentru gestionarea stării, Redux fiind o opțiune populară. Aruncă o privire asupra conceptelor și modelelor utilizate, deoarece este adesea o modalitate bună de a învăța ce probleme potențiale ai putea întâmpina în aplicațiile web mari și cum pot fi rezolvate.

Sarcină

Vom începe cu puțină refactorizare. Înlocuiește declarația account:

let account = null;

Cu:

let state = {
  account: null
};

Ideea este să centralizăm toate datele aplicației noastre într-un singur obiect de stare. Deocamdată avem doar account în stare, așa că nu se schimbă prea mult, dar creează o bază pentru evoluții.

De asemenea, trebuie să actualizăm funcțiile care îl folosesc. În funcțiile register() și login(), înlocuiește account = ... cu state.account = ...;

La începutul funcției updateDashboard(), adaugă această linie:

const account = state.account;

Această refactorizare în sine nu a adus multe îmbunătățiri, dar ideea a fost să punem bazele pentru următoarele schimbări.

Urmărirea modificărilor datelor

Acum că am pus în aplicare obiectul state pentru a stoca datele noastre, următorul pas este să centralizăm actualizările. Scopul este să fie mai ușor să urmărim orice modificări și când se întâmplă.

Pentru a evita modificările făcute direct asupra obiectului state, este, de asemenea, o practică bună să îl considerăm imutabil, ceea ce înseamnă că nu poate fi modificat deloc. Aceasta înseamnă că trebuie să creezi un nou obiect de stare dacă dorești să schimbi ceva în el. Procedând astfel, construiești o protecție împotriva efectelor secundare nedorite și deschizi posibilități pentru funcții noi în aplicația ta, cum ar fi implementarea funcțiilor de anulare/refacere, făcând totodată mai ușor de depanat. De exemplu, ai putea înregistra fiecare modificare făcută stării și să păstrezi un istoric al modificărilor pentru a înțelege sursa unei erori.

În JavaScript, poți utiliza Object.freeze() pentru a crea o versiune imutabilă a unui obiect. Dacă încerci să faci modificări unui obiect imutabil, va fi generată o excepție.

Știi diferența dintre un obiect imutabil superficial și unul profund? Poți citi despre asta aici.

Sarcină

Să creăm o nouă funcție updateState():

function updateState(property, newData) {
  state = Object.freeze({
    ...state,
    [property]: newData
  });
}

În această funcție, creăm un nou obiect de stare și copiem datele din starea anterioară folosind operatorul spread (...). Apoi, suprascriem o anumită proprietate a obiectului de stare cu noile date folosind notația cu paranteze [property] pentru atribuție. În cele din urmă, blocăm obiectul pentru a preveni modificările folosind Object.freeze(). Deocamdată avem doar proprietatea account stocată în stare, dar cu această abordare poți adăuga câte proprietăți ai nevoie.

Vom actualiza și inițializarea state pentru a ne asigura că starea inițială este, de asemenea, înghețată:

let state = Object.freeze({
  account: null
});

După aceea, actualizează funcția register înlocuind atribuirea state.account = result; cu:

updateState('account', result);

Fă același lucru cu funcția login, înlocuind state.account = data; cu:

updateState('account', data);

Vom profita acum de ocazie pentru a rezolva problema datelor contului care nu sunt șterse atunci când utilizatorul face clic pe Logout.

Creează o nouă funcție logout():

function logout() {
  updateState('account', null);
  navigate('/login');
}

În updateDashboard(), înlocuiește redirecționarea return navigate('/login'); cu return logout();

Încearcă să înregistrezi un cont nou, să te deconectezi și să te conectezi din nou pentru a verifica dacă totul funcționează corect.

Sfat: poți analiza toate modificările stării adăugând console.log(state) la sfârșitul funcției updateState() și deschizând consola în instrumentele de dezvoltare ale browserului.

Persistența stării

Majoritatea aplicațiilor web trebuie să păstreze datele pentru a funcționa corect. Toate datele critice sunt de obicei stocate într-o bază de date și accesate printr-un server API, cum ar fi datele contului utilizatorului în cazul nostru. Dar uneori, este interesant să păstrezi unele date în aplicația client care rulează în browser, pentru o experiență mai bună a utilizatorului sau pentru a îmbunătăți performanța încărcării.

Când dorești să păstrezi date în browser, există câteva întrebări importante pe care ar trebui să ți le pui:

  • Sunt datele sensibile? Ar trebui să eviți stocarea oricăror date sensibile pe client, cum ar fi parolele utilizatorilor.
  • Pentru cât timp ai nevoie să păstrezi aceste date? Plănuiești să accesezi aceste date doar pentru sesiunea curentă sau dorești să fie stocate permanent?

Există mai multe moduri de a stoca informații într-o aplicație web, în funcție de ceea ce dorești să realizezi. De exemplu, poți utiliza URL-urile pentru a stoca o interogare de căutare și a o face partajabilă între utilizatori. Poți utiliza, de asemenea, cookie-uri HTTP dacă datele trebuie partajate cu serverul, cum ar fi informațiile de autentificare.

O altă opțiune este să utilizezi una dintre numeroasele API-uri ale browserului pentru stocarea datelor. Două dintre ele sunt deosebit de interesante:

  • localStorage: un Key/Value store care permite păstrarea datelor specifice site-ului web curent între diferite sesiuni. Datele salvate în acesta nu expiră niciodată.
  • sessionStorage: funcționează la fel ca localStorage, cu excepția faptului că datele stocate în acesta sunt șterse atunci când sesiunea se încheie (când browserul este închis).

Reține că ambele API-uri permit doar stocarea de șiruri de caractere. Dacă dorești să stochezi obiecte complexe, va trebui să le serializezi în formatul JSON folosind JSON.stringify().

Dacă dorești să creezi o aplicație web care nu funcționează cu un server, este posibil să creezi o bază de date pe client utilizând API-ul IndexedDB. Acesta este rezervat pentru cazuri de utilizare avansate sau dacă trebuie să stochezi o cantitate semnificativă de date, deoarece este mai complex de utilizat.

Sarcină

Ne dorim ca utilizatorii noștri să rămână conectați până când fac clic explicit pe butonul Logout, așa că vom utiliza localStorage pentru a stoca datele contului. Mai întâi, să definim o cheie pe care o vom folosi pentru a stoca datele noastre.

const storageKey = 'savedAccount';

Apoi adaugă această linie la sfârșitul funcției updateState():

localStorage.setItem(storageKey, JSON.stringify(state.account));

Cu aceasta, datele contului utilizatorului vor fi păstrate și întotdeauna actualizate, deoarece am centralizat anterior toate actualizările stării. Aici începem să beneficiem de toate refactorizările anterioare 🙂.

Deoarece datele sunt salvate, trebuie să ne ocupăm și de restaurarea lor atunci când aplicația este încărcată. Deoarece vom începe să avem mai mult cod de inițializare, ar putea fi o idee bună să creăm o nouă funcție init, care să includă și codul nostru anterior de la sfârșitul fișierului app.js:

function init() {
  const savedAccount = localStorage.getItem(storageKey);
  if (savedAccount) {
    updateState('account', JSON.parse(savedAccount));
  }

  // Our previous initialization code
  window.onpopstate = () => updateRoute();
  updateRoute();
}

init();

Aici recuperăm datele salvate, iar dacă există, actualizăm starea în consecință. Este important să facem acest lucru înainte de a actualiza ruta, deoarece ar putea exista cod care se bazează pe starea în timpul actualizării paginii.

De asemenea, putem face pagina Dashboard pagina implicită a aplicației noastre, deoarece acum păstrăm datele contului. Dacă nu se găsesc date, tabloul de bord se ocupă de redirecționarea către pagina de Login oricum. În updateRoute(), înlocuiește fallback-ul return navigate('/login'); cu return navigate('/dashboard');.

Acum conectează-te în aplicație și încearcă să reîmprospătezi pagina. Ar trebui să rămâi pe tabloul de bord. Cu această actualizare, am rezolvat toate problemele inițiale...

Reîmprospătarea datelor

...Dar s-ar putea să fi creat și o problemă nouă. Ups!

Accesează tabloul de bord folosind contul test, apoi rulează această comandă într-un terminal pentru a crea o nouă tranzacție:

curl --request POST \
     --header "Content-Type: application/json" \
     --data "{ \"date\": \"2020-07-24\", \"object\": \"Bought book\", \"amount\": -20 }" \
     http://localhost:5000/api/accounts/test/transactions

Încearcă să reîmprospătezi pagina tabloului de bord în browser acum. Ce se întâmplă? Vezi noua tranzacție?

Starea este păstrată pe termen nelimitat datorită localStorage, dar asta înseamnă și că nu este niciodată actualizată până când nu te deconectezi și te conectezi din nou!

O strategie posibilă pentru a rezolva acest lucru este să reîncărcăm datele contului de fiecare dată când tabloul de bord este încărcat, pentru a evita datele învechite.

Sarcină

Creează o nouă funcție updateAccountData:

async function updateAccountData() {
  const account = state.account;
  if (!account) {
    return logout();
  }

  const data = await getAccount(account.user);
  if (data.error) {
    return logout();
  }

  updateState('account', data);
}

Această metodă verifică dacă utilizatorul este conectat, apoi reîncarcă datele contului de pe server.

Creează o altă funcție numită refresh:

async function refresh() {
  await updateAccountData();
  updateDashboard();
}

Aceasta actualizează datele contului, apoi se ocupă de actualizarea HTML-ului paginii tabloului de bord. Este ceea ce trebuie să apelăm atunci când ruta tabloului de bord este încărcată. Actualizează definiția rutei cu:

const routes = {
  '/login': { templateId: 'login' },
  '/dashboard': { templateId: 'dashboard', init: refresh }
};

Încearcă să reîncarci tabloul de bord acum, ar trebui să afișeze datele contului actualizate.


🚀 Provocare

Acum că reîncărcăm datele contului de fiecare dată când tabloul de bord este încărcat, crezi că mai este necesar să păstrăm toate datele contului?

Încearcă să lucrați împreună pentru a modifica ceea ce este salvat și încărcat din localStorage astfel încât să includă doar ceea ce este absolut necesar pentru ca aplicația să funcționeze.

Chestionar după curs

Chestionar post-lectură

Temă

Implementează dialogul „Adaugă tranzacție”

Iată un exemplu de rezultat după finalizarea temei:

Captură de ecran care arată un exemplu de dialog „Adaugă tranzacție”


Declinare de responsabilitate:
Acest document a fost tradus folosind serviciul de traducere AI Co-op Translator. Deși ne străduim să asigurăm acuratețea, vă rugăm să rețineți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.