|
3 weeks ago | |
---|---|---|
.. | ||
README.md | 3 weeks ago | |
assignment.md | 4 weeks ago |
README.md
Bygg en bankapp del 4: Konsepter for tilstandshåndtering
Quiz før forelesning
Introduksjon
Etter hvert som en webapplikasjon vokser, blir det en utfordring å holde oversikt over alle dataflyter. Hvilken kode henter data, hvilken side bruker den, hvor og når må den oppdateres...det er lett å ende opp med rotete kode som er vanskelig å vedlikeholde. Dette gjelder spesielt når du må dele data mellom forskjellige sider i appen din, for eksempel brukerdata. Konseptet tilstandshåndtering har alltid eksistert i alle typer programmer, men ettersom webapplikasjoner blir stadig mer komplekse, har det blitt et nøkkelpunkt å tenke på under utviklingen.
I denne siste delen skal vi se på appen vi har bygget for å revurdere hvordan tilstanden håndteres, slik at vi kan støtte nettleseroppdatering når som helst og bevare data på tvers av brukersesjoner.
Forutsetninger
Du må ha fullført datahenting-delen av webappen for denne leksjonen. Du må også installere Node.js og kjøre server-API lokalt slik at du kan administrere kontodata.
Du kan teste at serveren kjører riktig ved å utføre denne kommandoen i en terminal:
curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result
Revurder tilstandshåndtering
I forrige leksjon introduserte vi et grunnleggende konsept for tilstand i appen vår med den globale account
-variabelen som inneholder bankdataene for den innloggede brukeren. Men vår nåværende implementering har noen svakheter. Prøv å oppdatere siden når du er på dashbordet. Hva skjer?
Det er tre problemer med den nåværende koden:
- Tilstanden blir ikke bevart, da en nettleseroppdatering tar deg tilbake til innloggingssiden.
- Det finnes flere funksjoner som endrer tilstanden. Etter hvert som appen vokser, kan det bli vanskelig å holde oversikt over endringene, og det er lett å glemme å oppdatere én.
- Tilstanden blir ikke ryddet opp, så når du klikker på Logg ut, er kontodataene fortsatt der selv om du er på innloggingssiden.
Vi kunne oppdatert koden vår for å takle disse problemene én etter én, men det ville skapt mer kode duplisering og gjort appen mer kompleks og vanskelig å vedlikeholde. Eller vi kunne tatt oss noen minutter til å revurdere strategien vår.
Hvilke problemer prøver vi egentlig å løse her?
Tilstandshåndtering handler om å finne en god tilnærming for å løse disse to spesifikke problemene:
- Hvordan holde dataflytene i en app forståelige?
- Hvordan holde tilstandsdata alltid synkronisert med brukergrensesnittet (og vice versa)?
Når du har tatt hånd om disse, kan eventuelle andre problemer du har enten allerede være løst eller blitt enklere å fikse. Det finnes mange mulige tilnærminger for å løse disse problemene, men vi skal gå for en vanlig løsning som består av å sentralisere dataene og måtene å endre dem på. Dataflytene vil se slik ut:
Vi dekker ikke her delen der data automatisk utløser oppdatering av visningen, da det er knyttet til mer avanserte konsepter innen Reaktiv programmering. Det er et godt oppfølgingsemne hvis du ønsker å dykke dypere.
✅ Det finnes mange biblioteker der ute med forskjellige tilnærminger til tilstandshåndtering, Redux er et populært alternativ. Ta en titt på konseptene og mønstrene som brukes, da det ofte er en god måte å lære om potensielle problemer du kan møte i store webapplikasjoner og hvordan de kan løses.
Oppgave
Vi starter med litt refaktorering. Erstatt account
-deklarasjonen:
let account = null;
Med:
let state = {
account: null
};
Ideen er å sentralisere alle appdataene våre i et enkelt tilstandsobjekt. Vi har bare account
for nå i tilstanden, så det endrer ikke mye, men det skaper en vei for videre utvikling.
Vi må også oppdatere funksjonene som bruker det. I register()
- og login()
-funksjonene, erstatt account = ...
med state.account = ...
;
Øverst i updateDashboard()
-funksjonen, legg til denne linjen:
const account = state.account;
Denne refaktoreringen i seg selv har ikke gitt store forbedringer, men ideen var å legge grunnlaget for de neste endringene.
Spor dataendringer
Nå som vi har satt opp state
-objektet for å lagre dataene våre, er neste steg å sentralisere oppdateringene. Målet er å gjøre det enklere å holde oversikt over eventuelle endringer og når de skjer.
For å unngå at endringer gjøres direkte på state
-objektet, er det også en god praksis å betrakte det som uendelig, noe som betyr at det ikke kan endres i det hele tatt. Det betyr også at du må opprette et nytt tilstandsobjekt hvis du vil endre noe i det. Ved å gjøre dette, bygger du en beskyttelse mot potensielt uønskede sideeffekter, og åpner opp muligheter for nye funksjoner i appen din, som å implementere angre/gjenta, samtidig som det blir enklere å feilsøke. For eksempel kan du logge hver endring som gjøres i tilstanden og holde en historikk over endringene for å forstå kilden til en feil.
I JavaScript kan du bruke Object.freeze()
for å opprette en uendelig versjon av et objekt. Hvis du prøver å gjøre endringer på et uendelig objekt, vil det oppstå en unntak.
✅ Vet du forskjellen mellom et grunt og et dypt uendelig objekt? Du kan lese om det her.
Oppgave
La oss opprette en ny updateState()
-funksjon:
function updateState(property, newData) {
state = Object.freeze({
...state,
[property]: newData
});
}
I denne funksjonen oppretter vi et nytt tilstandsobjekt og kopierer data fra den forrige tilstanden ved hjelp av spread (...
) operatoren. Deretter overstyrer vi en bestemt egenskap i tilstandsobjektet med de nye dataene ved hjelp av brakettnotasjon [property]
for tildeling. Til slutt låser vi objektet for å forhindre endringer ved hjelp av Object.freeze()
. Vi har bare account
-egenskapen lagret i tilstanden for nå, men med denne tilnærmingen kan du legge til så mange egenskaper du trenger i tilstanden.
Vi oppdaterer også state
-initialiseringen for å sikre at den opprinnelige tilstanden også er låst:
let state = Object.freeze({
account: null
});
Etter dette, oppdater register
-funksjonen ved å erstatte state.account = result;
-tildelingen med:
updateState('account', result);
Gjør det samme med login
-funksjonen, og erstatt state.account = data;
med:
updateState('account', data);
Vi benytter også anledningen til å fikse problemet med at kontodataene ikke blir ryddet opp når brukeren klikker på Logg ut.
Opprett en ny funksjon logout()
:
function logout() {
updateState('account', null);
navigate('/login');
}
I updateDashboard()
, erstatt omdirigeringen return navigate('/login');
med return logout()
;
Prøv å registrere en ny konto, logge ut og inn igjen for å sjekke at alt fortsatt fungerer som det skal.
Tips: Du kan se på alle tilstandsendringer ved å legge til
console.log(state)
nederst iupdateState()
og åpne konsollen i nettleserens utviklingsverktøy.
Bevar tilstanden
De fleste webapplikasjoner trenger å bevare data for å kunne fungere korrekt. Alle kritiske data lagres vanligvis i en database og aksesseres via en server-API, som brukerkontodataene i vårt tilfelle. Men noen ganger kan det også være interessant å bevare noen data i klientappen som kjører i nettleseren, for en bedre brukeropplevelse eller for å forbedre lastingsytelsen.
Når du ønsker å bevare data i nettleseren, er det noen viktige spørsmål du bør stille deg selv:
- Er dataene sensitive? Du bør unngå å lagre sensitive data på klienten, som brukerpassord.
- Hvor lenge trenger du å beholde disse dataene? Planlegger du å aksessere disse dataene kun for den nåværende sesjonen, eller ønsker du at de skal lagres for alltid?
Det finnes flere måter å lagre informasjon i en webapp, avhengig av hva du ønsker å oppnå. For eksempel kan du bruke URL-er til å lagre et søkespørsmål og gjøre det delbart mellom brukere. Du kan også bruke HTTP-cookies hvis dataene må deles med serveren, som autentiseringsinformasjon.
En annen mulighet er å bruke en av de mange nettleser-API-ene for lagring av data. To av dem er spesielt interessante:
localStorage
: en Key/Value store som lar deg bevare data spesifikke for det nåværende nettstedet på tvers av forskjellige sesjoner. Dataene som lagres i det utløper aldri.sessionStorage
: denne fungerer på samme måte somlocalStorage
, bortsett fra at dataene som lagres i det blir slettet når sesjonen avsluttes (når nettleseren lukkes).
Merk at begge disse API-ene kun tillater lagring av strenger. Hvis du vil lagre komplekse objekter, må du serialisere dem til JSON-formatet ved hjelp av JSON.stringify()
.
✅ Hvis du ønsker å lage en webapp som ikke fungerer med en server, er det også mulig å opprette en database på klienten ved hjelp av IndexedDB
API. Dette er reservert for avanserte brukstilfeller eller hvis du trenger å lagre betydelige mengder data, da det er mer komplekst å bruke.
Oppgave
Vi ønsker at brukerne skal forbli innlogget til de eksplisitt klikker på Logg ut-knappen, så vi bruker localStorage
til å lagre kontodataene. Først definerer vi en nøkkel som vi skal bruke til å lagre dataene våre.
const storageKey = 'savedAccount';
Legg deretter til denne linjen på slutten av updateState()
-funksjonen:
localStorage.setItem(storageKey, JSON.stringify(state.account));
Med dette vil brukerkontodataene bli bevart og alltid være oppdatert ettersom vi tidligere sentraliserte alle tilstandsoppdateringene våre. Dette er hvor vi begynner å dra nytte av alle våre tidligere refaktorer 🙂.
Siden dataene er lagret, må vi også ta oss av å gjenopprette dem når appen lastes. Siden vi begynner å få mer initialiseringskode, kan det være en god idé å opprette en ny init
-funksjon, som også inkluderer vår tidligere kode nederst i 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();
Her henter vi de lagrede dataene, og hvis det finnes noen, oppdaterer vi tilstanden tilsvarende. Det er viktig å gjøre dette før vi oppdaterer ruten, da det kan være kode som er avhengig av tilstanden under sideoppdateringen.
Vi kan også gjøre Dashbord-siden til standard siden for applikasjonen vår, ettersom vi nå bevarer kontodataene. Hvis ingen data blir funnet, tar dashbordet seg av å omdirigere til Innlogging-siden uansett. I updateRoute()
, erstatt fallbacken return navigate('/login');
med return navigate('/dashboard');
.
Logg inn i appen og prøv å oppdatere siden. Du bør forbli på dashbordet. Med den oppdateringen har vi tatt hånd om alle våre opprinnelige problemer...
Oppdater dataene
...Men vi kan også ha skapt et nytt problem. Oops!
Gå til dashbordet ved hjelp av test
-kontoen, og kjør deretter denne kommandoen i en terminal for å opprette en ny transaksjon:
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
Prøv å oppdatere dashbordet i nettleseren nå. Hva skjer? Ser du den nye transaksjonen?
Tilstanden blir bevart på ubestemt tid takket være localStorage
, men det betyr også at den aldri blir oppdatert før du logger ut av appen og logger inn igjen!
En mulig strategi for å fikse dette er å laste inn kontodataene på nytt hver gang dashbordet lastes, for å unngå utdaterte data.
Oppgave
Opprett en ny funksjon 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);
}
Denne metoden sjekker at vi er innlogget og laster deretter kontodataene på nytt fra serveren.
Opprett en annen funksjon kalt refresh
:
async function refresh() {
await updateAccountData();
updateDashboard();
}
Denne oppdaterer kontodataene og tar deretter hånd om å oppdatere HTML-en på dashbordsiden. Det er det vi trenger å kalle når dashbordruten lastes. Oppdater rutedefinisjonen med:
const routes = {
'/login': { templateId: 'login' },
'/dashboard': { templateId: 'dashboard', init: refresh }
};
Prøv å oppdatere dashbordet nå, det bør vise de oppdaterte kontodataene.
🚀 Utfordring
Nå som vi laster inn kontodataene på nytt hver gang dashbordet lastes, tror du vi fortsatt trenger å bevare alle kontodataene?
Prøv å jobbe sammen for å endre hva som lagres og lastes fra localStorage
til kun å inkludere det som er absolutt nødvendig for at appen skal fungere.
Quiz etter forelesning
Oppgave
Implementer "Legg til transaksjon"-dialog
Her er et eksempelresultat etter å ha fullført oppgaven:
Ansvarsfraskrivelse:
Dette dokumentet er oversatt ved hjelp av AI-oversettelsestjenesten Co-op Translator. Selv om vi tilstreber nøyaktighet, vær oppmerksom på at automatiske oversettelser kan inneholde feil eller unøyaktigheter. Det originale dokumentet på sitt opprinnelige språk bør anses som den autoritative kilden. For kritisk informasjon anbefales profesjonell menneskelig oversettelse. Vi er ikke ansvarlige for eventuelle misforståelser eller feiltolkninger som oppstår ved bruk av denne oversettelsen.