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/hr/7-bank-project/4-state-management
leestott c0ca49b2cc
🌐 Update translations via Co-op Translator
3 weeks ago
..
README.md 🌐 Update translations via Co-op Translator 3 weeks ago
assignment.md 🌐 Update translations via Co-op Translator 3 weeks ago

README.md

Izrada bankovne aplikacije, dio 4: Koncepti upravljanja stanjem

Kviz prije predavanja

Kviz prije predavanja

Uvod

Kako web aplikacija raste, postaje izazovno pratiti sve tokove podataka. Koji kod dobiva podatke, koja stranica ih koristi, gdje i kada ih treba ažurirati... lako je završiti s neurednim kodom koji je teško održavati. Ovo je posebno istinito kada trebate dijeliti podatke između različitih stranica vaše aplikacije, primjerice korisničke podatke. Koncept upravljanja stanjem oduvijek je postojao u svim vrstama programa, ali kako web aplikacije postaju sve složenije, sada je ključno razmišljati o tome tijekom razvoja.

U ovom završnom dijelu, pregledat ćemo aplikaciju koju smo izgradili kako bismo preispitali način upravljanja stanjem, omogućujući podršku za osvježavanje preglednika u bilo kojem trenutku i zadržavanje podataka tijekom korisničkih sesija.

Preduvjeti

Potrebno je da ste završili dio dohvaćanja podataka za ovu lekciju. Također trebate instalirati Node.js i pokrenuti API poslužitelj lokalno kako biste mogli upravljati podacima računa.

Možete testirati radi li poslužitelj ispravno izvršavanjem ove naredbe u terminalu:

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

Preispitivanje upravljanja stanjem

U prethodnoj lekciji uveli smo osnovni koncept stanja u našoj aplikaciji s globalnom varijablom account koja sadrži bankovne podatke za trenutno prijavljenog korisnika. Međutim, naša trenutna implementacija ima neke nedostatke. Pokušajte osvježiti stranicu dok ste na nadzornoj ploči. Što se događa?

Postoje tri problema s trenutnim kodom:

  • Stanje nije zadržano, jer vas osvježavanje preglednika vraća na stranicu za prijavu.
  • Postoji više funkcija koje mijenjaju stanje. Kako aplikacija raste, to može otežati praćenje promjena i lako je zaboraviti ažurirati neku od njih.
  • Stanje se ne čisti, pa kada kliknete na Odjava, podaci o računu i dalje ostaju prisutni iako ste na stranici za prijavu.

Mogli bismo ažurirati naš kod kako bismo riješili ove probleme jedan po jedan, ali to bi stvorilo više dupliciranja koda i učinilo aplikaciju složenijom i težom za održavanje. Ili bismo mogli zastati na nekoliko minuta i preispitati našu strategiju.

Koje probleme zapravo pokušavamo riješiti?

Upravljanje stanjem odnosi se na pronalaženje dobrog pristupa za rješavanje ova dva specifična problema:

  • Kako održati tokove podataka u aplikaciji razumljivima?
  • Kako osigurati da su podaci o stanju uvijek sinkronizirani s korisničkim sučeljem (i obrnuto)?

Kada se pobrinete za ovo, bilo koji drugi problemi koje biste mogli imati možda će već biti riješeni ili će ih biti lakše riješiti. Postoji mnogo mogućih pristupa za rješavanje ovih problema, ali mi ćemo se odlučiti za uobičajeno rješenje koje se sastoji od centralizacije podataka i načina na koje ih mijenjamo. Tokovi podataka izgledali bi ovako:

Shema koja prikazuje tokove podataka između HTML-a, korisničkih akcija i stanja

Ovdje nećemo pokriti dio gdje podaci automatski pokreću ažuriranje prikaza, jer je to povezano s naprednijim konceptima reaktivnog programiranja. To je dobra tema za daljnje istraživanje ako želite dublje zaroniti.

Postoji mnogo biblioteka s različitim pristupima upravljanju stanjem, a Redux je popularna opcija. Pogledajte koncepte i obrasce koji se koriste jer je to često dobar način za učenje o potencijalnim problemima s kojima se možete suočiti u velikim web aplikacijama i kako ih riješiti.

Zadatak

Počet ćemo s malo refaktoriranja. Zamijenite deklaraciju account:

let account = null;

S:

let state = {
  account: null
};

Ideja je centralizirati sve podatke naše aplikacije u jedan objekt stanja. Trenutno imamo samo account u stanju, tako da se puno toga ne mijenja, ali to stvara put za buduće nadogradnje.

Također moramo ažurirati funkcije koje ga koriste. U funkcijama register() i login() zamijenite account = ... s state.account = ...;

Na vrhu funkcije updateDashboard() dodajte ovu liniju:

const account = state.account;

Ovo refaktoriranje samo po sebi nije donijelo mnogo poboljšanja, ali ideja je bila postaviti temelje za sljedeće promjene.

Praćenje promjena podataka

Sada kada smo postavili objekt state za pohranu naših podataka, sljedeći korak je centralizirati ažuriranja. Cilj je olakšati praćenje bilo kakvih promjena i kada se one događaju.

Kako bismo izbjegli promjene na objektu state, također je dobra praksa smatrati ga nepromjenjivim, što znači da ga uopće nije moguće mijenjati. To također znači da morate stvoriti novi objekt stanja ako želite promijeniti bilo što u njemu. Na taj način gradite zaštitu od potencijalno neželjenih nuspojava i otvarate mogućnosti za nove značajke u svojoj aplikaciji, poput implementacije poništavanja/ponovnog izvršavanja, dok također olakšavate otklanjanje pogrešaka. Na primjer, mogli biste zabilježiti svaku promjenu napravljenu na stanju i zadržati povijest promjena kako biste razumjeli izvor pogreške.

U JavaScriptu možete koristiti Object.freeze() za stvaranje nepromjenjive verzije objekta. Ako pokušate napraviti promjene na nepromjenjivom objektu, bit će podignuta iznimka.

Znate li razliku između plitkog i dubokog nepromjenjivog objekta? Možete pročitati o tome ovdje.

Zadatak

Kreirajmo novu funkciju updateState():

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

U ovoj funkciji stvaramo novi objekt stanja i kopiramo podatke iz prethodnog stanja koristeći spread (...) operator. Zatim prepisujemo određeno svojstvo objekta stanja s novim podacima koristeći notaciju zagrada [property] za dodjelu. Na kraju, zaključavamo objekt kako bismo spriječili izmjene koristeći Object.freeze(). Trenutno imamo samo svojstvo account pohranjeno u stanju, ali s ovim pristupom možete dodati koliko god svojstava trebate u stanje.

Također ćemo ažurirati inicijalizaciju state kako bismo osigurali da je početno stanje također zamrznuto:

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

Nakon toga, ažurirajte funkciju register zamjenom state.account = result; s:

updateState('account', result);

Isto učinite s funkcijom login, zamjenjujući state.account = data; s:

updateState('account', data);

Sada ćemo iskoristiti priliku da riješimo problem s podacima o računu koji se ne brišu kada korisnik klikne na Odjava.

Kreirajte novu funkciju logout():

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

U updateDashboard() zamijenite preusmjeravanje return navigate('/login'); s return logout();;

Pokušajte registrirati novi račun, odjaviti se i ponovno prijaviti kako biste provjerili radi li sve ispravno.

Savjet: možete pogledati sve promjene stanja dodavanjem console.log(state) na dno updateState() i otvaranjem konzole u alatima za razvoj preglednika.

Zadržavanje stanja

Većina web aplikacija treba zadržati podatke kako bi ispravno radila. Svi kritični podaci obično se pohranjuju u bazu podataka i pristupa im se putem API-ja poslužitelja, poput podataka o korisničkom računu u našem slučaju. No, ponekad je također zanimljivo zadržati neke podatke na klijentskoj aplikaciji koja se pokreće u vašem pregledniku, za bolje korisničko iskustvo ili za poboljšanje performansi učitavanja.

Kada želite zadržati podatke u svom pregledniku, postoji nekoliko važnih pitanja koja biste trebali postaviti:

  • Jesu li podaci osjetljivi? Trebali biste izbjegavati pohranjivanje bilo kakvih osjetljivih podataka na klijentu, poput korisničkih lozinki.
  • Koliko dugo trebate zadržati te podatke? Planirate li pristupiti tim podacima samo za trenutnu sesiju ili želite da budu pohranjeni zauvijek?

Postoji više načina za pohranjivanje informacija unutar web aplikacije, ovisno o tome što želite postići. Na primjer, možete koristiti URL-ove za pohranjivanje upita za pretraživanje i učiniti ih dijeljivima između korisnika. Također možete koristiti HTTP kolačiće ako se podaci trebaju dijeliti s poslužiteljem, poput informacija za autentifikaciju.

Druga opcija je korištenje jedne od mnogih API-ja preglednika za pohranjivanje podataka. Dvije od njih su posebno zanimljive:

  • localStorage: Key/Value pohrana koja omogućuje zadržavanje podataka specifičnih za trenutnu web stranicu tijekom različitih sesija. Podaci spremljeni u njoj nikada ne istječu.
  • sessionStorage: radi isto kao localStorage, osim što se podaci pohranjeni u njoj brišu kada sesija završi (kada se preglednik zatvori).

Napomena: oba ova API-ja omogućuju pohranjivanje samo nizova. Ako želite pohraniti složene objekte, morat ćete ih serijalizirati u JSON format koristeći JSON.stringify().

Ako želite stvoriti web aplikaciju koja ne radi s poslužiteljem, također je moguće stvoriti bazu podataka na klijentu koristeći IndexedDB API. Ovo je rezervirano za napredne slučajeve upotrebe ili ako trebate pohraniti značajnu količinu podataka, jer je složenije za korištenje.

Zadatak

Želimo da naši korisnici ostanu prijavljeni dok izričito ne kliknu na gumb Odjava, pa ćemo koristiti localStorage za pohranjivanje podataka o računu. Prvo, definirajmo ključ koji ćemo koristiti za pohranjivanje naših podataka.

const storageKey = 'savedAccount';

Zatim dodajte ovu liniju na kraj funkcije updateState():

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

S ovim će se podaci o korisničkom računu zadržati i uvijek biti ažurirani jer smo prethodno centralizirali sva ažuriranja stanja. Ovo je trenutak kada počinjemo imati koristi od svih naših prethodnih refaktora 🙂.

Budući da su podaci spremljeni, također se moramo pobrinuti za njihovo vraćanje kada se aplikacija učita. Budući da ćemo početi imati više koda za inicijalizaciju, možda bi bilo dobro stvoriti novu funkciju init, koja također uključuje naš prethodni kod na dnu 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();

Ovdje dohvaćamo spremljene podatke, a ako ih ima, ažuriramo stanje u skladu s tim. Važno je to učiniti prije ažuriranja rute, jer bi moglo postojati koda koji se oslanja na stanje tijekom ažuriranja stranice.

Također možemo učiniti stranicu Nadzorna ploča zadanom stranicom naše aplikacije, jer sada zadržavamo podatke o računu. Ako se ne pronađu podaci, nadzorna ploča ionako preusmjerava na stranicu Prijava. U updateRoute() zamijenite zadano return navigate('/login'); s return navigate('/dashboard');.

Sada se prijavite u aplikaciju i pokušajte osvježiti stranicu. Trebali biste ostati na nadzornoj ploči. S tim ažuriranjem riješili smo sve naše početne probleme...

Osvježavanje podataka

...Ali možda smo također stvorili novi problem. Ups!

Idite na nadzornu ploču koristeći račun test, a zatim pokrenite ovu naredbu u terminalu kako biste stvorili novu transakciju:

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

Pokušajte sada osvježiti stranicu nadzorne ploče u pregledniku. Što se događa? Vidite li novu transakciju?

Stanje se zadržava neograničeno zahvaljujući localStorage, ali to također znači da se nikada ne ažurira dok se ne odjavite iz aplikacije i ponovno prijavite!

Jedna moguća strategija za rješavanje ovog problema je ponovno učitavanje podataka o računu svaki put kada se učita nadzorna ploča, kako bi se izbjegli zastarjeli podaci.

Zadatak

Kreirajte novu funkciju 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);
}

Ova metoda provjerava jesmo li trenutno prijavljeni, a zatim ponovno učitava podatke o računu s poslužitelja.

Kreirajte drugu funkciju pod nazivom refresh:

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

Ova funkcija ažurira podatke o računu, a zatim se brine o ažuriranju HTML-a stranice nadzorne ploče. To je ono što trebamo pozvati kada se učita ruta nadzorne ploče. Ažurirajte definiciju rute s:

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

Pokušajte sada ponovno učitati nadzornu ploču, trebala bi prikazati ažurirane podatke o računu.


🚀 Izazov

Sada kada ponovno učitavamo podatke o računu svaki put kada se učita nadzorna ploča, mislite li da još uvijek trebamo zadržavati sve podatke o računu?

Pokušajte zajedno promijeniti što se sprema i učitava iz localStorage tako da uključuje samo ono što je apsolutno potrebno za rad aplikacije.

Kviz nakon predavanja

Kviz nakon predavanja

Zadatak

Implementirajte dijalog "Dodaj transakciju"

Evo primjera rezultata nakon dovršetka zadatka:

Snimka zaslona koja prikazuje primjer dijaloga "Dodaj transakciju"


Odricanje od odgovornosti:
Ovaj dokument je preveden korištenjem AI usluge za prevođenje Co-op Translator. Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakva nesporazuma ili pogrešna tumačenja koja proizlaze iz korištenja ovog prijevoda.