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/sv/7-bank-project/4-state-management/README.md

27 KiB

Bygg en bankapp del 4: Koncept för tillståndshantering

Förhandsquiz

Förhandsquiz

Introduktion

Tillståndshantering är som navigationssystemet på Voyager-rymdfarkosten när allt fungerar smidigt märker du knappt att det finns där. Men när något går fel blir det skillnaden mellan att nå interstellärt utrymme och att driva vilse i den kosmiska tomheten. Inom webbutveckling representerar tillstånd allt som din applikation behöver komma ihåg: användarens inloggningsstatus, formulärdata, navigeringshistorik och tillfälliga gränssnittstillstånd.

När din bankapp har utvecklats från ett enkelt inloggningsformulär till en mer sofistikerad applikation har du förmodligen stött på några vanliga utmaningar. Uppdatera sidan och användarna loggas ut oväntat. Stäng webbläsaren och allt framsteg försvinner. Försök att felsöka ett problem och du letar genom flera funktioner som alla ändrar samma data på olika sätt.

Detta är inte tecken på dålig kodning det är de naturliga växtsmärtor som uppstår när applikationer når en viss komplexitet. Varje utvecklare möter dessa utmaningar när deras appar går från "proof of concept" till "produktionsklara."

I den här lektionen kommer vi att implementera ett centraliserat tillståndshanteringssystem som förvandlar din bankapp till en pålitlig, professionell applikation. Du kommer att lära dig att hantera dataflöden förutsägbart, bevara användarsessioner på rätt sätt och skapa den smidiga användarupplevelse som moderna webbapplikationer kräver.

Förkunskaper

Innan du dyker in i koncepten för tillståndshantering behöver du ha din utvecklingsmiljö korrekt inställd och grunden för din bankapp på plats. Den här lektionen bygger direkt på koncepten och koden från tidigare delar i den här serien.

Se till att du har följande komponenter redo innan du fortsätter:

Nödvändig inställning:

Testa din miljö:

Verifiera att din API-server fungerar korrekt genom att köra detta kommando i en terminal:

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

Vad detta kommando gör:

  • Skickar en GET-förfrågan till din lokala API-server
  • Testar anslutningen och verifierar att servern svarar
  • Returnerar API-versioninformation om allt fungerar korrekt

Diagnostisera nuvarande tillståndsproblem

Som Sherlock Holmes som undersöker en brottsplats behöver vi förstå exakt vad som händer i vår nuvarande implementation innan vi kan lösa mysteriet med försvinnande användarsessioner.

Låt oss genomföra ett enkelt experiment som avslöjar de underliggande utmaningarna med tillståndshantering:

🧪 Prova detta diagnostiska test:

  1. Logga in på din bankapp och navigera till instrumentpanelen
  2. Uppdatera webbläsarsidan
  3. Observera vad som händer med din inloggningsstatus

Om du omdirigeras tillbaka till inloggningsskärmen har du upptäckt det klassiska problemet med tillståndsbevarande. Detta beteende uppstår eftersom vår nuvarande implementation lagrar användardata i JavaScript-variabler som återställs vid varje sidladdning.

Nuvarande implementeringsproblem:

Den enkla account-variabeln från vår tidigare lektion skapar tre betydande problem som påverkar både användarupplevelse och kodunderhåll:

Problem Teknisk orsak Användarpåverkan
Sessionförlust Siduppdatering rensar JavaScript-variabler Användare måste autentisera sig ofta
Spridda uppdateringar Flera funktioner ändrar tillstånd direkt Felsökning blir allt svårare
Ofullständig rensning Utloggning rensar inte alla tillståndsreferenser Potentiella säkerhets- och integritetsproblem

Den arkitektoniska utmaningen:

Som Titanics fackindelade design som verkade robust tills flera fack fylldes med vatten samtidigt, kommer det inte att lösa de underliggande arkitektoniska problemen att fixa dessa problem individuellt. Vi behöver en omfattande lösning för tillståndshantering.

💡 Vad försöker vi egentligen åstadkomma här?

Tillståndshantering handlar egentligen om att lösa två grundläggande pussel:

  1. Var är mina data?: Hålla reda på vilken information vi har och var den kommer ifrån
  2. Är alla på samma sida?: Säkerställa att det användarna ser stämmer överens med vad som faktiskt händer

Vår plan:

Istället för att jaga vår egen svans ska vi skapa ett centraliserat tillståndshanteringssystem. Tänk på det som att ha en riktigt organiserad person som ansvarar för allt viktigt:

Schema som visar dataflöden mellan HTML, användaråtgärder och tillstånd

Förstå detta dataflöde:

  • Centraliserar allt applikationstillstånd på en plats
  • Styr alla tillståndsförändringar genom kontrollerade funktioner
  • Säkerställer att användargränssnittet förblir synkroniserat med det aktuella tillståndet
  • Tillhandahåller ett tydligt, förutsägbart mönster för datahantering

💡 Professionell insikt: Den här lektionen fokuserar på grundläggande koncept. För komplexa applikationer erbjuder bibliotek som Redux mer avancerade funktioner för tillståndshantering. Att förstå dessa grundprinciper hjälper dig att bemästra vilket tillståndshanteringsbibliotek som helst.

⚠️ Avancerat ämne: Vi kommer inte att täcka automatiska UI-uppdateringar som triggas av tillståndsförändringar, eftersom detta involverar koncept från reaktiv programmering. Se detta som ett utmärkt nästa steg för din lärande resa!

Uppgift: Centralisera tillståndsstruktur

Låt oss börja med att omvandla vår spridda tillståndshantering till ett centraliserat system. Detta första steg lägger grunden för alla förbättringar som följer.

Steg 1: Skapa ett centraliserat tillståndsobjekt

Byt ut den enkla account-deklarationen:

let account = null;

Mot ett strukturerat tillståndsobjekt:

let state = {
  account: null
};

Varför denna förändring är viktig:

  • Centraliserar all applikationsdata på en plats
  • Förbereder strukturen för att lägga till fler tillståndsegenskaper senare
  • Skapar en tydlig gräns mellan tillstånd och andra variabler
  • Etablerar ett mönster som skalar när din app växer

Steg 2: Uppdatera tillgångsmönster för tillstånd

Uppdatera dina funktioner för att använda den nya tillståndsstrukturen:

I funktionerna register() och login(), ersätt:

account = ...

Med:

state.account = ...

I funktionen updateDashboard(), lägg till denna rad högst upp:

const account = state.account;

Vad dessa uppdateringar åstadkommer:

  • Bibehåller befintlig funktionalitet samtidigt som strukturen förbättras
  • Förbereder din kod för mer sofistikerad tillståndshantering
  • Skapar konsekventa mönster för att komma åt tillståndsdata
  • Etablerar grunden för centraliserade tillståndsuppdateringar

💡 Notera: Denna omstrukturering löser inte omedelbart våra problem, men den skapar den nödvändiga grunden för de kraftfulla förbättringar som kommer härnäst!

Implementera kontrollerade tillståndsuppdateringar

Med vårt tillstånd centraliserat är nästa steg att etablera kontrollerade mekanismer för datamodifikationer. Detta tillvägagångssätt säkerställer förutsägbara tillståndsförändringar och enklare felsökning.

Den grundläggande principen liknar flygtrafikledning: istället för att tillåta flera funktioner att ändra tillstånd oberoende, kommer vi att kanalisera alla förändringar genom en enda, kontrollerad funktion. Detta mönster ger tydlig översikt över när och hur data ändras.

Oföränderlig tillståndshantering:

Vi kommer att behandla vårt state-objekt som oföränderligt, vilket innebär att vi aldrig ändrar det direkt. Istället skapar varje förändring ett nytt tillståndsobjekt med de uppdaterade data.

Även om detta tillvägagångssätt initialt kan verka ineffektivt jämfört med direkta ändringar, ger det betydande fördelar för felsökning, testning och att upprätthålla applikationens förutsägbarhet.

Fördelar med oföränderlig tillståndshantering:

Fördel Beskrivning Påverkan
Förutsägbarhet Förändringar sker endast genom kontrollerade funktioner Lättare att felsöka och testa
Historikspårning Varje tillståndsförändring skapar ett nytt objekt Möjliggör ångra/göra om-funktionalitet
Förebyggande av sidoeffekter Inga oavsiktliga ändringar Förhindrar mystiska buggar
Prestandaoptimering Lätt att upptäcka när tillstånd faktiskt ändrades Möjliggör effektiva UI-uppdateringar

JavaScript-oföränderlighet med Object.freeze():

JavaScript tillhandahåller Object.freeze() för att förhindra objektmodifikationer:

const immutableState = Object.freeze({ account: userData });
// Any attempt to modify immutableState will throw an error

Vad som händer här:

  • Förhindrar direkta egenskapsändringar eller borttagningar
  • Kastar undantag vid försök till modifikation
  • Säkerställer att tillståndsförändringar måste gå genom kontrollerade funktioner
  • Skapar ett tydligt kontrakt för hur tillstånd kan uppdateras

💡 Fördjupning: Läs om skillnaden mellan ytlig och djup oföränderlighet i MDN-dokumentationen. Att förstå denna skillnad är avgörande för komplexa tillståndsstrukturer.

Uppgift

Låt oss skapa en ny funktion updateState():

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

I denna funktion skapar vi ett nytt tillståndsobjekt och kopierar data från det tidigare tillståndet med hjälp av spridningsoperatorn (...). Sedan skriver vi över en specifik egenskap i tillståndsobjektet med de nya data med hjälp av haknotation [property] för tilldelning. Slutligen låser vi objektet för att förhindra modifikationer med Object.freeze(). Vi har bara egenskapen account lagrad i tillståndet för tillfället, men med detta tillvägagångssätt kan du lägga till så många egenskaper som du behöver i tillståndet.

Vi kommer också att uppdatera initialiseringen av state för att säkerställa att det initiala tillståndet också är låst:

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

Efter det, uppdatera funktionen register genom att ersätta tilldelningen state.account = result; med:

updateState('account', result);

Gör samma sak med funktionen login, ersätt state.account = data; med:

updateState('account', data);

Vi tar nu tillfället i akt att åtgärda problemet med att kontodata inte rensas när användaren klickar på Logga ut.

Skapa en ny funktion logout():

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

I updateDashboard(), ersätt omdirigeringen return navigate('/login'); med return logout();;

Prova att registrera ett nytt konto, logga ut och in igen för att kontrollera att allt fortfarande fungerar korrekt.

Tips: Du kan titta på alla tillståndsförändringar genom att lägga till console.log(state) längst ner i updateState() och öppna konsolen i webbläsarens utvecklingsverktyg.

Implementera databevarande

Problemet med sessionförlust som vi identifierade tidigare kräver en bevarandelösning som upprätthåller användartillstånd över webbläsarsessioner. Detta förvandlar vår applikation från en tillfällig upplevelse till ett pålitligt, professionellt verktyg.

Tänk på hur atomklockor bibehåller exakt tid även vid strömavbrott genom att lagra kritiskt tillstånd i icke-flyktigt minne. På samma sätt behöver webbapplikationer persistenta lagringsmekanismer för att bevara viktig användardata över webbläsarsessioner och siduppdateringar.

Strategiska frågor för databevarande:

Innan du implementerar bevarande, överväg dessa kritiska faktorer:

Fråga Bankappens kontext Beslutsinverkan
Är datan känslig? Kontosaldo, transaktionshistorik Välj säkra lagringsmetoder
Hur länge ska det bevaras? Inloggningsstatus vs. tillfälliga UI-inställningar Välj lämplig lagringsvaraktighet
Behöver servern det? Autentiseringstoken vs. UI-inställningar Bestäm delningskrav

Webbläsarens lagringsalternativ:

Moderna webbläsare tillhandahåller flera lagringsmekanismer, var och en designad för olika användningsområden:

Primära lagrings-API:er:

  1. localStorage: Persistent Nyckel/Värde-lagring

    • Bevarar data över webbläsarsessioner på obestämd tid
    • Överlever webbläsaromstarter och datoromstarter
    • Avgränsad till den specifika webbplatsens domän
    • Perfekt för användarinställningar och inloggningsstatus
  2. sessionStorage: Tillfällig sessionslagring

    • Fungerar identiskt med localStorage under aktiva sessioner
    • Rensas automatiskt när webbläsarfliken stängs
    • Idealisk för tillfälliga data som inte bör bevaras
  3. HTTP Cookies: Serverdelad lagring

    • Skickas automatiskt med varje serverförfrågan
    • Perfekt för autentiseringstoken
    • Begränsad i storlek och kan påverka prestanda

Krav på dataserialisering:

Både localStorage och sessionStorage lagrar endast strängar:

// Convert objects to JSON strings for storage
const accountData = { user: 'john', balance: 150 };
localStorage.setItem('account', JSON.stringify(accountData));

// Parse JSON strings back to objects when retrieving
const savedAccount = JSON.parse(localStorage.getItem('account'));

Förstå serialisering:

  • Konverterar JavaScript-objekt till JSON-strängar med JSON.stringify()
  • Återskapar objekt från JSON med JSON.parse()
  • Hanterar komplexa, inbäddade objekt och arrayer automatiskt
  • Misslyckas med funktioner, odefinierade värden och cirkulära referenser

💡 Avancerat alternativ: För komplexa offline-applikationer med stora dataset, överväg att använda IndexedDB API. Det erbjuder en fullständig databas på klientsidan men kräver en mer komplex implementation.

Uppgift: Implementera localStorage-persistens

Låt oss implementera en persistent lagring så att användare förblir inloggade tills de uttryckligen loggar ut. Vi kommer att använda localStorage för att lagra kontodata mellan webbläsarsessioner.

Steg 1: Definiera lagringskonfiguration

const storageKey = 'savedAccount';

Vad denna konstant tillhandahåller:

  • Skapar en konsekvent identifierare för vår lagrade data
  • Förhindrar skrivfel i referenser till lagringsnycklar
  • Gör det enkelt att ändra lagringsnyckeln vid behov
  • Följer bästa praxis för underhållbar kod

Steg 2: Lägg till automatisk persistens

Lägg till denna rad i slutet av funktionen updateState():

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

Genomgång av vad som händer här:

  • Konverterar kontoobjektet till en JSON-sträng för lagring
  • Sparar datan med vår konsekventa lagringsnyckel
  • Utförs automatiskt när tillståndsändringar sker
  • Säkerställer att lagrad data alltid är synkroniserad med aktuellt tillstånd

💡 Arkitektonisk fördel: Eftersom vi centraliserade alla tillståndsuppdateringar via updateState(), krävdes endast en rad kod för att lägga till persistens. Detta visar styrkan i goda arkitektoniska beslut!

Steg 3: Återställ tillstånd vid appstart

Skapa en initialiseringsfunktion för att återställa sparad data:

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

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

init();

Förståelse för initialiseringsprocessen:

  • Hämtar tidigare sparad kontodata från localStorage
  • Parserar JSON-strängen tillbaka till ett JavaScript-objekt
  • Uppdaterar tillståndet med vår kontrollerade uppdateringsfunktion
  • Återställer användarens session automatiskt vid sidladdning
  • Utförs innan ruttuppdateringar för att säkerställa att tillståndet är tillgängligt

Steg 4: Optimera standardrutt

Uppdatera standardrutten för att dra nytta av persistens:

I updateRoute(), ersätt:

// Replace: return navigate('/login');
return navigate('/dashboard');

Varför denna ändring är logisk:

  • Utnyttjar vårt nya persistenssystem effektivt
  • Tillåter dashboarden att hantera autentiseringskontroller
  • Omdirigerar automatiskt till inloggning om ingen sparad session finns
  • Skapar en mer sömlös användarupplevelse

Testa din implementation:

  1. Logga in på din bankapp
  2. Uppdatera webbläsarsidan
  3. Verifiera att du förblir inloggad och på dashboarden
  4. Stäng och öppna webbläsaren igen
  5. Navigera tillbaka till din app och bekräfta att du fortfarande är inloggad

🎉 Prestation uppnådd: Du har framgångsrikt implementerat persistent tillståndshantering! Din app beter sig nu som en professionell webbapplikation.

Balans mellan persistens och dataintegritet

Vårt persistenssystem upprätthåller användarsessioner framgångsrikt, men introducerar en ny utmaning: data som blir inaktuell. När flera användare eller applikationer ändrar samma serverdata blir lokal cache-information föråldrad.

Denna situation liknar vikinganavigatörer som förlitade sig på både lagrade stjärnkartor och aktuella observationer av himlakroppar. Kartorna gav konsistens, men navigatörerna behövde färska observationer för att ta hänsyn till förändrade förhållanden. På samma sätt behöver vår applikation både persistent användartillstånd och aktuell serverdata.

🧪 Upptäck problemet med inaktuell data:

  1. Logga in på dashboarden med kontot test
  2. Kör detta kommando i en terminal för att simulera en transaktion från en annan källa:
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
  1. Uppdatera din dashboard-sida i webbläsaren
  2. Observera om du ser den nya transaktionen

Vad detta test visar:

  • Visar hur localStorage kan bli "föråldrad" (inaktuell)
  • Simulerar verkliga scenarier där dataändringar sker utanför din app
  • Avslöjar spänningen mellan persistens och dataintegritet

Utmaningen med inaktuell data:

Problem Orsak Användarpåverkan
Föråldrad data localStorage löper aldrig ut automatiskt Användare ser inaktuell information
Serverändringar Andra appar/användare ändrar samma data Inkonsekventa vyer över plattformar
Cache vs. verklighet Lokal cache matchar inte serverns tillstånd Dålig användarupplevelse och förvirring

Lösningsstrategi:

Vi kommer att implementera ett "uppdatera vid laddning"-mönster som balanserar fördelarna med persistens med behovet av aktuell data. Denna metod bibehåller en smidig användarupplevelse samtidigt som den säkerställer datanoggrannhet.

Uppgift: Implementera datauppdateringssystem

Vi kommer att skapa ett system som automatiskt hämtar färsk data från servern samtidigt som vi behåller fördelarna med vår persistenta tillståndshantering.

Steg 1: Skapa en uppdateringsfunktion för kontodata

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);
}

Förstå logiken i denna funktion:

  • Kontrollerar om en användare för närvarande är inloggad (state.account finns)
  • Omdirigerar till utloggning om ingen giltig session hittas
  • Hämtar färsk kontodata från servern med den befintliga funktionen getAccount()
  • Hanterar serverfel på ett smidigt sätt genom att logga ut ogiltiga sessioner
  • Uppdaterar tillståndet med färsk data via vårt kontrollerade uppdateringssystem
  • Triggar automatisk localStorage-persistens via funktionen updateState()

Steg 2: Skapa en uppdateringshanterare för dashboarden

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

Vad denna uppdateringsfunktion åstadkommer:

  • Samordnar datauppdatering och UI-uppdateringsprocessen
  • Väntar på att färsk data ska laddas innan displayen uppdateras
  • Säkerställer att dashboarden visar den mest aktuella informationen
  • Bibehåller en tydlig separation mellan datahantering och UI-uppdateringar

Steg 3: Integrera med ruttsystemet

Uppdatera din ruttkonfiguration för att trigga uppdatering automatiskt:

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

Hur denna integration fungerar:

  • Utför uppdateringsfunktionen varje gång dashboard-rutten laddas
  • Säkerställer att färsk data alltid visas när användare navigerar till dashboarden
  • Bibehåller den befintliga ruttstrukturen samtidigt som dataintegritet läggs till
  • Tillhandahåller ett konsekvent mönster för rutt-specifik initialisering

Testa ditt datauppdateringssystem:

  1. Logga in på din bankapp
  2. Kör curl-kommandot från tidigare för att skapa en ny transaktion
  3. Uppdatera din dashboard-sida eller navigera bort och tillbaka
  4. Verifiera att den nya transaktionen visas omedelbart

🎉 Perfekt balans uppnådd: Din app kombinerar nu den smidiga upplevelsen av persistent tillstånd med noggrannheten hos färsk serverdata!

GitHub Copilot Agent-utmaning 🚀

Använd Agent-läget för att slutföra följande utmaning:

Beskrivning: Implementera ett omfattande tillståndshanteringssystem med ångra/gör om-funktionalitet för bankappen. Denna utmaning hjälper dig att öva på avancerade koncept inom tillståndshantering, inklusive historikspårning, immutabla uppdateringar och synkronisering av användargränssnittet.

Prompt: Skapa ett förbättrat tillståndshanteringssystem som inkluderar: 1) En historikarray som spårar alla tidigare tillstånd, 2) Ångra och gör om-funktioner som kan återgå till tidigare tillstånd, 3) UI-knappar för ångra/gör om-operationer på dashboarden, 4) En maximal historikgräns på 10 tillstånd för att förhindra minnesproblem, och 5) Rätt städning av historik när användaren loggar ut. Säkerställ att ångra/gör om-funktionaliteten fungerar med kontosaldoförändringar och kvarstår över webbläsaruppdateringar.

Läs mer om agent mode här.

🚀 Utmaning: Lagringsoptimering

Din implementation hanterar nu användarsessioner, datauppdateringar och tillståndshantering effektivt. Men överväg om vår nuvarande metod optimalt balanserar lagringseffektivitet med funktionalitet.

Precis som schackmästare som skiljer mellan viktiga pjäser och utbytbara bönder, kräver effektiv tillståndshantering att man identifierar vilken data som måste bevaras kontra vilken som alltid bör hämtas färsk från servern.

Optimeringsanalys:

Utvärdera din nuvarande localStorage-implementation och överväg dessa strategiska frågor:

  • Vilken är den minsta informationen som krävs för att upprätthålla användarautentisering?
  • Vilken data ändras tillräckligt ofta för att lokal caching ger liten nytta?
  • Hur kan lagringsoptimering förbättra prestanda utan att försämra användarupplevelsen?

Implementeringsstrategi:

  • Identifiera den väsentliga data som måste bevaras (troligen bara användaridentifiering)
  • Modifiera din localStorage-implementation för att endast lagra kritisk sessionsdata
  • Säkerställ att färsk data alltid hämtas från servern vid dashboardbesök
  • Testa att din optimerade metod bibehåller samma användarupplevelse

Avancerad övervägning:

  • Jämför avvägningarna mellan att lagra fullständig kontodata kontra endast autentiseringstokens
  • Dokumentera dina beslut och resonemang för framtida teammedlemmar

Denna utmaning hjälper dig att tänka som en professionell utvecklare som överväger både användarupplevelse och applikationseffektivitet. Ta dig tid att experimentera med olika tillvägagångssätt!

Quiz efter föreläsningen

Quiz efter föreläsningen

Uppgift

Implementera dialogen "Lägg till transaktion"

Här är ett exempelresultat efter att ha slutfört uppgiften:

Skärmdump som visar ett exempel på dialogen "Lägg till transaktion"


Ansvarsfriskrivning:
Detta dokument har översatts med hjälp av AI-översättningstjänsten Co-op Translator. Även om vi strävar efter noggrannhet, bör det noteras att automatiserade översättningar kan innehålla fel eller felaktigheter. Det ursprungliga dokumentet på dess ursprungliga språk bör betraktas som den auktoritativa källan. För kritisk information rekommenderas professionell mänsklig översättning. Vi ansvarar inte för eventuella missförstånd eller feltolkningar som uppstår vid användning av denna översättning.