Skip to content

Latest commit

 

History

History
799 lines (591 loc) · 37.2 KB

File metadata and controls

799 lines (591 loc) · 37.2 KB

Budowanie aplikacji bankowej Część 4: Koncepcje zarządzania stanem

⚡ Co możesz zrobić w następnych 5 minutach

Szybka ścieżka startowa dla zapracowanych programistów

flowchart LR
    A[⚡ 5 minut] --> B[Diagnozuj problemy ze stanem]
    B --> C[Utwórz centralny obiekt stanu]
    C --> D[Dodaj funkcję updateState]
    D --> E[Zobacz natychmiastowe ulepszenia]
Loading
  • Minuta 1: Przetestuj aktualny problem stanu - zaloguj się, odśwież stronę, obserwuj wylogowanie
  • Minuta 2: Zamień let account = null na let state = { account: null }
  • Minuta 3: Stwórz prostą funkcję updateState() do kontrolowanych aktualizacji
  • Minuta 4: Zaktualizuj jedną funkcję, aby używała nowego wzorca
  • Minuta 5: Przetestuj poprawioną przewidywalność i łatwość debugowania

Szybki test diagnostyczny:

// Przed: Rozproszony stan
let account = null; // Utracone po odświeżeniu!

// Po: Centralny stan
let state = Object.freeze({ account: null }); // Kontrolowany i śledzony!

Dlaczego to ważne: W 5 minut doświadczysz transformacji z chaotycznego zarządzania stanem do przewidywalnych, łatwych do debugowania wzorców. To podstawa, która sprawia, że skomplikowane aplikacje są utrzymywalne.

🗺️ Twoja droga nauki przez mistrzostwo zarządzania stanem

journey
    title Od Rozproszonego Stanu do Profesjonalnej Architektury
    section Diagnozowanie Problemów
      Identify state loss issues: 3: You
      Understand scattered updates: 4: You
      Recognize architecture needs: 6: You
    section Centralizacja Kontroli
      Create unified state object: 5: You
      Implement controlled updates: 7: You
      Add immutable patterns: 8: You
    section Dodawanie Trwałości
      Implement localStorage: 6: You
      Handle serialization: 7: You
      Create session continuity: 9: You
    section Równoważenie Świeżości
      Address data staleness: 5: You
      Build refresh systems: 8: You
      Achieve optimal balance: 9: You
Loading

Cel Twojej podróży: Pod koniec tej lekcji zbudujesz profesjonalny system zarządzania stanem, który obsługuje utrwalanie, świeżość danych i przewidywalne aktualizacje - te same wzorce stosowane w aplikacjach produkcyjnych.

Quiz przed wykładem

Quiz przed wykładem

Wprowadzenie

Zarządzanie stanem jest jak system nawigacyjny na statku Voyager – gdy wszystko działa płynnie, ledwo zauważasz jego obecność. Ale gdy coś idzie nie tak, to różnica między dotarciem do przestrzeni międzygwiezdnej a dryfowaniem bez celu w kosmicznej pustce. W tworzeniu stron internetowych stan reprezentuje wszystko, co Twoja aplikacja musi zapamiętać: status logowania użytkownika, dane formularza, historię nawigacji i tymczasowe stany interfejsu.

W miarę jak Twoja aplikacja bankowa ewoluowała od prostego formularza logowania do bardziej zaawansowanej aplikacji, prawdopodobnie napotkałeś typowe wyzwania. Odśwież stronę, a użytkownicy są niespodziewanie wylogowani. Zamknij przeglądarkę, a cały postęp znika. Debuggując problem, przeszukujesz wiele funkcji, które modyfikują te same dane na różne sposoby.

To nie są oznaki złego programowania – to naturalne trudności rozwojowe pojawiające się, gdy aplikacje osiągają pewien poziom złożoności. Każdy programista staje wobec tych wyzwań, gdy jego aplikacje przechodzą z "protokołu koncepcji" do "gotowości produkcyjnej".

W tej lekcji zaimplementujemy scentralizowany system zarządzania stanem, który przekształci Twoją aplikację bankową w niezawodną, profesjonalną aplikację. Nauczysz się zarządzać przepływami danych przewidywalnie, utrwalać sesje użytkowników w odpowiedni sposób oraz tworzyć płynne doświadczenie użytkownika, którego wymagają nowoczesne aplikacje webowe.

Wymagania wstępne

Zanim zagłębisz się w koncepcje zarządzania stanem, musisz mieć poprawnie skonfigurowane środowisko deweloperskie i gotową podstawę aplikacji bankowej. Ta lekcja bazuje bezpośrednio na koncepcjach i kodzie z poprzednich części tego cyklu.

Upewnij się, że masz gotowe następujące elementy przed kontynuacją:

Wymagane przygotowanie:

  • Ukończ lekcję pobierania danych – Twoja aplikacja powinna poprawnie ładować i wyświetlać dane konta
  • Zainstaluj Node.js na swoim systemie do uruchomienia backendowego API
  • Uruchom lokalnie API serwera do obsługi operacji na danych konta

Testowanie środowiska:

Sprawdź, czy serwer API działa poprawnie, wykonując tę komendę w terminalu:

curl http://localhost:5000/api
# -> powinno zwrócić jako rezultat "Bank API v1.0.0"

Co robi ta komenda:

  • Wysyła żądanie GET do lokalnego serwera API
  • Testuje połączenie i sprawdza, czy serwer odpowiada
  • Zwraca informacje o wersji API, jeśli wszystko działa prawidłowo

🧠 Przegląd architektury zarządzania stanem

mindmap
  root((Zarządzanie Stanem))
    Current Problems
      Session Loss
        Page Refresh Issues
        Browser Close Impact
        Variable Reset Problems
      Scattered Updates
        Multiple Modification Points
        Debugging Challenges
        Unpredictable Behavior
      Incomplete Cleanup
        Logout State Issues
        Memory Leaks
        Security Concerns
    Centralized Solutions
      Unified State Object
        Single Source of Truth
        Predictable Structure
        Scalable Foundation
      Controlled Updates
        Immutable Patterns
        Object.freeze Usage
        Function-Based Changes
      State Tracking
        History Management
        Debug Visibility
        Change Auditing
    Persistence Strategies
      localStorage Integration
        Session Continuity
        JSON Serialization
        Automatic Sync
      Data Freshness
        Server Refresh
        Stale Data Handling
        Balance Optimization
      Storage Optimization
        Minimal Data
        Performance Focus
        Security Considerations
Loading

Główna zasada: Profesjonalne zarządzanie stanem to równowaga między przewidywalnością, utrwalaniem danych i wydajnością, aby tworzyć niezawodne doświadczenia użytkownika, które skalują się od prostych interakcji do złożonych przepływów aplikacji.


Diagnozowanie bieżących problemów ze stanem

Jak Sherlock Holmes badający miejsce zbrodni, musimy dokładnie zrozumieć, co dzieje się w naszej obecnej implementacji, zanim rozwiążemy zagadkę znikających sesji użytkowników.

Przeprowadźmy prosty eksperyment, który ujawni wyzwania związane z zarządzaniem stanem:

🧪 Wypróbuj ten test diagnostyczny:

  1. Zaloguj się do aplikacji bankowej i przejdź do pulpitu nawigacyjnego
  2. Odśwież stronę przeglądarki
  3. Obserwuj, co dzieje się z Twoim statusem logowania

Jeśli zostaniesz przekierowany z powrotem do ekranu logowania, odkryłeś klasyczny problem z utrwalaniem stanu. Takie zachowanie wynika z faktu, że nasza obecna implementacja przechowuje dane użytkownika w zmiennych JavaScript, które resetują się przy każdym załadowaniu strony.

Problemy aktualnej implementacji:

Prosta zmienna account z naszej poprzedniej lekcji powoduje trzy istotne problemy wpływające na doświadczenie użytkownika i utrzymywalność kodu:

Problem Przyczyna techniczna Wpływ na użytkownika
Utrata sesji Odświeżenie strony czyści zmienne JavaScript Użytkownicy muszą się często ponownie logować
Rozsiane aktualizacje Wiele funkcji modyfikuje stan bezpośrednio Debugowanie staje się coraz trudniejsze
Niepełne czyszczenie Wylogowanie nie usuwa wszystkich referencji do stanu Potencjalne problemy z bezpieczeństwem i prywatnością

Wyzwanie architektoniczne:

Podobnie jak projekt Titanica z podziałem na przedziały, który wydawał się solidny aż do zalania ich wielu jednocześnie, naprawianie tych problemów pojedynczo nie rozwiąże podstawowej kwestii architektonicznej. Potrzebujemy kompleksowego rozwiązania zarządzania stanem.

💡 Co właściwie chcemy osiągnąć?

Zarządzanie stanem to tak naprawdę rozwiązanie dwóch fundamentalnych zagadek:

  1. Gdzie są moje dane?: Śledzenie, jakie informacje posiadamy i skąd pochodzą
  2. Czy wszyscy mają tę samą informację?: Upewnienie się, że to, co widzą użytkownicy, odpowiada rzeczywistości

Nasz plan działania:

Zamiast biegać w kółko, stworzymy system scentralizowanego zarządzania stanem. Wyobraź to sobie jak jedną naprawdę zorganizowaną osobę odpowiedzialną za wszystko, co ważne:

Schemat pokazujący przepływ danych między HTML, akcjami użytkownika i stanem

flowchart TD
    A[Działanie użytkownika] --> B[Obsługa zdarzenia]
    B --> C[Funkcja updateState]
    C --> D{Weryfikacja stanu}
    D -->|Poprawny| E[Utwórz nowy stan]
    D -->|Niepoprawny| F[Obsługa błędu]
    E --> G[Object.freeze]
    G --> H[Aktualizacja localStorage]
    H --> I[Wywołaj aktualizację UI]
    I --> J[Użytkownik widzi zmiany]
    F --> K[Użytkownik widzi błąd]
    
    subgraph "Warstwa zarządzania stanem"
        C
        E
        G
    end
    
    subgraph "Warstwa trwałości"
        H
        L[localStorage]
        H -.-> L
    end
Loading

Zrozumienie tego przepływu danych:

  • Scentralizowany cały stan aplikacji w jednym miejscu
  • Kieruje wszystkie zmiany stanu przez kontrolowane funkcje
  • Zapewnia synchronizację UI z aktualnym stanem
  • Dostarcza wyraźny, przewidywalny wzorzec zarządzania danymi

💡 Profesjonalna wskazówka: Ta lekcja skupia się na podstawowych koncepcjach. W przypadku złożonych aplikacji biblioteki takie jak Redux oferują bardziej zaawansowane funkcje zarządzania stanem. Zrozumienie tych podstawowych zasad pomoże Ci opanować dowolną bibliotekę zarządzania stanem.

⚠️ Zaawansowany temat: Nie omówimy automatycznych aktualizacji UI wywoływanych przez zmiany stanu, ponieważ wiąże się to z koncepcjami programowania reaktywnego. To doskonały kolejny krok na Twojej drodze nauki!

Zadanie: Scentralizuj strukturę stanu

Zacznijmy przekształcać nasze rozsiane zarządzanie stanem w system scentralizowany. Ten pierwszy krok ustanawia fundament pod wszystkie kolejne ulepszenia.

Krok 1: Utwórz centralny obiekt stanu

Zamień prostą deklarację account:

let account = null;

Na ustrukturyzowany obiekt stanu:

let state = {
  account: null
};

Dlaczego ta zmiana ma znaczenie:

  • Scentralizowuje wszystkie dane aplikacji w jednym miejscu
  • Przygotowuje strukturę do dodania kolejnych właściwości stanu
  • Tworzy wyraźną granicę między stanem a innymi zmiennymi
  • Ustanawia wzorzec skalowalny wraz z rozwojem aplikacji

Krok 2: Zaktualizuj wzorce dostępu do stanu

Zaktualizuj swoje funkcje, aby korzystały z nowej struktury stanu:

W funkcjach register() i login() zamień:

account = ...

Na:

state.account = ...

W funkcji updateDashboard() dodaj na początku tę linię:

const account = state.account;

Co osiągają te aktualizacje:

  • Utrzymują istniejącą funkcjonalność, poprawiając strukturę
  • Przygotowują kod na bardziej zaawansowane zarządzanie stanem
  • Tworzą spójne wzorce dostępu do danych stanu
  • Ustanawiają fundament dla scentralizowanych aktualizacji stanu

💡 Uwaga: Ten refaktoring nie rozwiązuje od razu naszych problemów, ale tworzy niezbędną bazę dla potężnych ulepszeń, które nadejdą!

🎯 Sprawdzenie pedagogiczne: zasady centralizacji

Zatrzymaj się i pomyśl: właśnie zaimplementowałeś fundament scentralizowanego zarządzania stanem. To kluczowa decyzja architektoniczna.

Szybka samoocena:

  • Czy potrafisz wyjaśnić, dlaczego centralizacja stanu w jednym obiekcie jest lepsza niż rozsiane zmienne?
  • Co się stanie, jeśli zapomnisz zaktualizować funkcję, aby używała state.account?
  • Jak ten wzorzec przygotowuje Twój kod na bardziej zaawansowane funkcje?

Powiązanie z rzeczywistością: Wzorzec centralizacji, którego się nauczyłeś, to podstawa nowoczesnych frameworków takich jak Redux, Vuex czy React Context. Budujesz to samo myślenie architektoniczne, które stosują duże aplikacje.

Pytanie wyzwania: Gdybyś musiał dodać preferencje użytkownika (motyw, język) do swojej aplikacji, gdzie byś je dodał w strukturze stanu? Jak by to skalowało?

Implementacja kontrolowanych aktualizacji stanu

Mając stan scentralizowany, kolejnym krokiem jest ustanowienie kontrolowanych mechanizmów modyfikacji danych. Podejście to zapewnia przewidywalne zmiany stanu i łatwiejsze debugowanie.

Podstawowa zasada przypomina kontrolę ruchu lotniczego: zamiast pozwalać wielu funkcjom na niezależne modyfikowanie stanu, przekażemy wszystkie zmiany przez pojedynczą, kontrolowaną funkcję. Ten wzorzec zapewnia jasny nadzór nad tym, kiedy i jak zmieniają się dane.

Niezmienność stanu:

Będziemy traktować nasz obiekt state jako niezmienny, co oznacza, że nigdy nie modyfikujemy go bezpośrednio. Zamiast tego każda zmiana tworzy nowy obiekt stanu z zaktualizowanymi danymi.

Choć takie podejście na początku może wydawać się mniej wydajne niż modyfikacje bezpośrednie, oferuje znaczne korzyści podczas debugowania, testowania i utrzymywania przewidywalności aplikacji.

Korzyści z niezmiennego zarządzania stanem:

Korzyść Opis Wpływ
Przewidywalność Zmiany zachodzą tylko przez kontrolowane funkcje Łatwiejsze debugowanie i testowanie
Śledzenie historii Każda zmiana stanu tworzy nowy obiekt Umożliwia funkcje cofania/powtarzania
Zapobieganie efektom ubocznym Brak przypadkowych modyfikacji Zapobiega tajemniczym błędom
Optymalizacja wydajności Łatwo wykryć faktyczne zmiany stanu Umożliwia efektywne aktualizacje UI

Niezmienność w JavaScript z Object.freeze():

JavaScript oferuje Object.freeze(), aby zapobiec modyfikacjom obiektów:

const immutableState = Object.freeze({ account: userData });
// Każda próba modyfikacji immutableState spowoduje wyrzucenie błędu

Co się tutaj dzieje:

  • Uniemożliwia bezpośrednie przypisywanie lub usuwanie właściwości
  • Wyrzuca wyjątki, jeśli podejmowane są próby modyfikacji
  • Zapewnia, że zmiany stanu muszą przechodzić przez kontrolowane funkcje
  • Tworzy wyraźny kontrakt, jak można aktualizować stan

💡 Głębsze zanurzenie: Dowiedz się o różnicy między płytką a głęboką niezmiennością obiektów w dokumentacji MDN. Zrozumienie tej różnicy jest kluczowe dla złożonych struktur stanu.

stateDiagram-v2
    [*] --> StateV1: Stan początkowy
    StateV1 --> StateV2: updateState('account', newData)
    StateV2 --> StateV3: updateState('account', anotherUpdate)
    StateV3 --> StateV4: updateState('preferences', userSettings)
    
    note right of StateV1
        Object.freeze()
        Niezmienny
        Możliwy do debugowania
    end note
    
    note right of StateV2
        Utworzono nowy obiekt
        Poprzedni stan zachowany
        Przewidywalne zmiany
    end note
Loading

Zadanie

Stwórzmy nową funkcję updateState():

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

W tej funkcji tworzymy nowy obiekt stanu i kopiujemy dane ze stanu poprzedniego używając operatora rozproszenia (...). Następnie nadpisujemy konkretną właściwość obiektu stanu nowymi danymi, używając notacji nawiasowej [property] do przypisania. Na końcu blokujemy obiekt, by zapobiec modyfikacjom, używając Object.freeze(). Na razie w stanie przechowujemy tylko właściwość account, ale z tym podejściem możesz dodać tyle właściwości, ile potrzebujesz.

Zaktualizujemy również inicjalizację state, aby początkowy stan był również zamrożony:

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

Następnie w funkcji register zamień przypisanie state.account = result; na:

updateState('account', result);

To samo zrób w funkcji login, zamieniając state.account = data; na:

updateState('account', data);

Teraz skorzystamy z okazji, aby naprawić problem z nieczyszczeniem danych konta, gdy użytkownik kliknie Wyloguj się.

Stwórz nową funkcję logout():

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

W updateDashboard() zamień przekierowanie return navigate('/login'); na return logout();

Spróbuj zarejestrować nowe konto, wylogować się i zalogować ponownie, aby upewnić się, że wszystko działa poprawnie.

Wskazówka: możesz obserwować wszystkie zmiany stanu, dodając console.log(state) na końcu updateState() i otwierając konsolę narzędzi programistycznych przeglądarki.

Implementacja utrwalania danych

Problem utraty sesji, który wcześniej zidentyfikowaliśmy, wymaga rozwiązania w postaci utrwalania, które zachowa stan użytkownika pomiędzy sesjami przeglądarki. To przekształca naszą aplikację z tymczasowego doświadczenia w niezawodne, profesjonalne narzędzie.

Pomyśl o tym, jak zegary atomowe utrzymują precyzyjny czas nawet podczas przerw w zasilaniu, przechowując krytyczny stan w pamięci nieulotnej. Podobnie aplikacje webowe potrzebują mechanizmów trwałego przechowywania, aby zachować podstawowe dane użytkownika między sesjami i odświeżeniami strony.

Strategiczne pytania dotyczące utrwalania danych:

Zanim zaimplementujesz utrwalanie, rozważ te kluczowe czynniki:

Pytanie Kontekst aplikacji bankowej Wpływ na decyzję
Czy dane są wrażliwe? Saldo konta, historia transakcji Wybierz bezpieczne metody przechowywania
Jak długo powinno to przechowywać? Stan zalogowania vs. tymczasowe preferencje UI Wybierz odpowiedni czas przechowywania
Czy serwer tego potrzebuje? Tokeny uwierzytelniania vs. ustawienia UI Określ wymagania dotyczące udostępniania

Opcje przechowywania w przeglądarce:

Nowoczesne przeglądarki oferują kilka mechanizmów przechowywania, każdy zaprojektowany do różnych zastosowań:

Główne API przechowywania:

  1. localStorage: trwałe przechowywanie klucz/wartość

    • Przechowuje dane przez wiele sesji przeglądarki bezterminowo
    • Przetrwa restarty przeglądarki i ponowne uruchomienia komputera
    • Zakres ograniczony do konkretnej domeny witryny
    • Idealne do preferencji użytkownika i stanu zalogowania
  2. sessionStorage: tymczasowe przechowywanie sesji

    • Działa tak samo jak localStorage podczas aktywnej sesji
    • Usuwa się automatycznie po zamknięciu zakładki przeglądarki
    • Świetne do tymczasowych danych, które nie powinny być przechowywane
  3. HTTP Cookies: współdzielone z serwerem przechowywanie

    • Wysyłane automatycznie z każdym żądaniem do serwera
    • Idealne do tokenów uwierzytelniania
    • Ograniczone rozmiarowo i mogą wpływać na wydajność

Wymóg serializacji danych:

Zarówno localStorage, jak i sessionStorage przechowują tylko łańcuchy znaków:

// Konwertuj obiekty na łańcuchy JSON do przechowywania
const accountData = { user: 'john', balance: 150 };
localStorage.setItem('account', JSON.stringify(accountData));

// Parsuj łańcuchy JSON z powrotem na obiekty podczas pobierania
const savedAccount = JSON.parse(localStorage.getItem('account'));

Zrozumienie serializacji:

  • Konwertuje obiekty JavaScript na łańcuchy JSON za pomocą JSON.stringify()
  • Odtwarza obiekty z JSON za pomocą JSON.parse()
  • Obsługuje złożone zagnieżdżone obiekty i tablice automatycznie
  • Nie działa na funkcjach, niezdefiniowanych wartościach i odwołaniach cyklicznych

💡 Opcja zaawansowana: Dla złożonych aplikacji offline z dużymi zestawami danych rozważ API IndexedDB. Zapewnia pełną bazę danych po stronie klienta, ale wymaga bardziej skomplikowanej implementacji.

quadrantChart
    title Opcje przechowywania w przeglądarce
    x-axis Niska złożoność --> Wysoka złożoność
    y-axis Krótki czas --> Długi czas
    
    quadrant-1 Narzędzia profesjonalne
    quadrant-2 Prosta trwałość
    quadrant-3 Przechowywanie tymczasowe
    quadrant-4 Zaawansowane systemy
    
    localStorage: [0.3, 0.8]
    sessionStorage: [0.2, 0.2]
    HTTP Cookies: [0.6, 0.7]
    IndexedDB: [0.9, 0.9]
    Memory Variables: [0.1, 0.1]
Loading

Zadanie: Implementacja trwałości w localStorage

Zaimplementujmy trwałe przechowywanie, aby użytkownicy pozostawali zalogowani, dopóki nie wylogują się świadomie. Użyjemy localStorage do przechowywania danych konta między sesjami przeglądarki.

Krok 1: Definiowanie konfiguracji przechowywania

const storageKey = 'savedAccount';

Co dostarcza ta stała:

  • Tworzy spójny identyfikator dla naszych przechowywanych danych
  • Zapobiega błędom literowym przy odwołaniach do klucza przechowywania
  • Ułatwia zmianę klucza przechowywania w razie potrzeby
  • Stosuje się do najlepszych praktyk dla łatwego utrzymania kodu

Krok 2: Dodanie automatycznej trwałości

Dodaj ten wiersz na końcu funkcji updateState():

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

Analiza tego, co się tutaj dzieje:

  • Konwertuje obiekt konta na łańcuch JSON do przechowywania
  • Zapisuje dane używając spójnego klucza przechowywania
  • Wykonuje się automatycznie przy każdej zmianie stanu
  • Zapewnia, że przechowywane dane są zawsze zsynchronizowane ze stanem

💡 Korzyść architektoniczna: Ponieważ centralizowaliśmy wszystkie aktualizacje stanu przez updateState(), dodanie trwałości wymagało tylko jednej linijki kodu. To pokazuje siłę dobrych decyzji architektonicznych!

Krok 3: Przywracanie stanu przy uruchomieniu aplikacji

Utwórz funkcję inicjalizacyjną do przywracania zapisanych danych:

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

  // Nasz poprzedni kod inicjalizacyjny
  window.onpopstate = () => updateRoute();
  updateRoute();
}

init();

Zrozumienie procesu inicjalizacji:

  • Pobiera wcześniej zapisane dane konta z localStorage
  • Parsuje łańcuch JSON z powrotem na obiekt JavaScript
  • Aktualizuje stan poprzez naszą kontrolowaną funkcję aktualizacji
  • Automatycznie przywraca sesję użytkownika po załadowaniu strony
  • Wykonuje się przed aktualizacją tras, aby stan był dostępny

Krok 4: Optymalizacja domyślnej trasy

Zaktualizuj domyślną trasę, by wykorzystać trwałość:

W updateRoute() zamień:

// Zamień na: return navigate('/login');
return navigate('/dashboard');

Dlaczego ta zmiana ma sens:

  • Wykorzystuje skutecznie nasz nowy system trwałości
  • Pozwala dashboardowi obsługiwać kontrole uwierzytelniania
  • Przekierowuje do logowania automatycznie, jeśli nie ma zapisanej sesji
  • Tworzy bardziej płynne doświadczenie użytkownika

Testowanie Twojej implementacji:

  1. Zaloguj się do swojej aplikacji bankowej
  2. Odśwież stronę w przeglądarce
  3. Sprawdź, czy pozostajesz zalogowany i na dashboardzie
  4. Zamknij i ponownie otwórz przeglądarkę
  5. Wróć do aplikacji i potwierdź, że nadal jesteś zalogowany

🎉 Osiągnięcie odblokowane: Pomyślnie zaimplementowałeś zarządzanie trwałym stanem! Twoja aplikacja teraz działa jak profesjonalne rozwiązanie webowe.

🎯 Kontrola pedagogiczna: Architektura trwałości

Zrozumienie architektury: Zaimplementowałeś zaawansowaną warstwę trwałości, która równoważy doświadczenie użytkownika i złożoność zarządzania danymi.

Opanowane kluczowe koncepcje:

  • Serializacja JSON: konwersja złożonych obiektów do przechowywalnych łańcuchów znaków
  • Automatyczna synchronizacja: zmiany stanu wyzwalają trwałe przechowywanie
  • Odzyskiwanie sesji: aplikacje mogą przywracać kontekst użytkownika po przerwach
  • Centralizacja trwałości: jedna funkcja update zarządza całym przechowywaniem

Powiązanie branżowe: Ten wzorzec trwałości jest fundamentem dla Progressive Web Apps (PWA), aplikacji offline-first i nowoczesnych doświadczeń mobilnych. Budujesz rozwiązanie produkcyjne.

Pytanie do refleksji: Jak zmodyfikowałbyś ten system, by obsługiwał wiele kont użytkowników na tym samym urządzeniu? Weź pod uwagę prywatność i bezpieczeństwo.

Równoważenie trwałości z aktualnością danych

Nasz system trwałości skutecznie utrzymuje sesje użytkowników, ale wprowadza nowy problem: nieświeżość danych. Gdy wielu użytkowników lub aplikacji modyfikuje te same dane na serwerze, lokalny cache staje się przestarzały.

Ta sytuacja przypomina nawigatorów Wikingów, którzy korzystali zarówno z przechowywanych map gwiazd jak i bieżących obserwacji nieba. Mapy zapewniały spójność, ale nawigatorzy potrzebowali świeżych obserwacji by uwzględnić zmienne warunki. Podobnie nasza aplikacja potrzebuje zarówno trwałego stanu użytkownika, jak i aktualnych danych serwera.

🧪 Odkrywanie problemu świeżości danych:

  1. Zaloguj się na dashboardie na konto test
  2. Uruchom to polecenie w terminalu, aby zasymulować transakcję z innego źródła:
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. Odśwież stronę dashboardu w przeglądarce
  2. Zaobserwuj, czy pojawiła się nowa transakcja

Co pokazuje ten test:

  • Pokazuje, jak localStorage może się stać "przestarzałe"
  • Symuluje realne scenariusze zmian danych poza Twoją aplikacją
  • Ujawnia napięcie między trwałością a świeżością danych

Wyzwanie nieświeżości danych:

Problem Przyczyna Skutek dla użytkownika
Przestarzałe dane localStorage nigdy nie wygasa automatycznie Użytkownicy widzą nieaktualne informacje
Zmiany serwera Inne aplikacje/użytkownicy modyfikują te same dane Niespójne widoki na różnych platformach
Cache vs. rzeczywistość Lokalny cache nie odpowiada stanowi serwera Zła jakość doświadczenia i dezorientacja

Strategia rozwiązania:

Wdrożymy wzorzec „odśwież przy ładowaniu”, który łączy zalety trwałości z potrzebą świeżych danych. Podejście to utrzymuje płynność doświadczenia użytkownika i zapewnia poprawność danych.

sequenceDiagram
    participant U as Użytkownik
    participant A as Aplikacja
    participant L as localStorage
    participant S as Serwer
    
    U->>A: Otwiera aplikację
    A->>L: Załaduj zapisany stan
    L-->>A: Zwróć dane z pamięci podręcznej
    A->>U: Pokaż interfejs natychmiast
    A->>S: Pobierz świeże dane
    S-->>A: Zwróć aktualne dane
    A->>L: Zaktualizuj pamięć podręczną
    A->>U: Zaktualizuj interfejs ze świeżymi danymi
Loading

Zadanie: Implementacja systemu odświeżania danych

Stworzymy system, który automatycznie pobiera świeże dane z serwera, zachowując zalety naszego trwałego zarządzania stanem.

Krok 1: Utwórz aktualizator danych konta

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

Logika tej funkcji:

  • Sprawdza, czy użytkownik jest aktualnie zalogowany (istnieje state.account)
  • Przekierowuje do wylogowania, jeśli sesja jest nieważna
  • Pobiera świeże dane konta z serwera używając istniejącej funkcji getAccount()
  • Obsługuje błędy serwera, wylogowując nieprawidłowe sesje
  • Aktualizuje stan za pomocą naszego kontrolowanego systemu aktualizacji
  • Wyzwala automatyczną trwałość w localStorage poprzez updateState()

Krok 2: Utwórz obsługę odświeżania dashboardu

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

Co ta funkcja odświeżania osiąga:

  • Koordynuje proces odświeżania danych i aktualizacji UI
  • Oczekuje na załadowanie świeżych danych przed aktualizacją wyświetlacza
  • Zapewnia, że dashboard pokazuje najnowsze informacje
  • Utrzymuje rozdział zarządzania danymi od aktualizacji UI

Krok 3: Integracja z systemem tras

Zaktualizuj konfigurację tras, by automatycznie wywoływała odświeżanie:

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

Jak działa ta integracja:

  • Wykonuje funkcję odświeżania za każdym razem, gdy wczytuje się trasa dashboardu
  • Zapewnia, że zawsze wyświetlane są świeże dane przy nawigacji do dashboardu
  • Utrzymuje istniejącą strukturę trasy, dodając jednocześnie aktualność danych
  • Dostarcza spójny wzorzec inicjalizacji specyficznej dla trasy

Testowanie Twojego systemu odświeżania danych:

  1. Zaloguj się do aplikacji bankowej
  2. Uruchom wcześniej podane polecenie curl, aby utworzyć nową transakcję
  3. Odśwież stronę dashboardu lub przejdź gdziekolwiek i wróć
  4. Sprawdź, czy nowa transakcja pojawiła się od razu

🎉 Osiągnięto doskonałą równowagę: Twoja aplikacja łączy teraz płynność trwałego stanu z dokładnością świeżych danych z serwera!

📈 Twoja oś czasu mistrzostwa zarządzania stanem

timeline
    title Profesjonalna podróż zarządzania stanem
    
    section Rozpoznanie problemu
        Diagnostyka problemów ze stanem
            : Identyfikacja problemów z utratą sesji
            : Zrozumienie problemów z rozproszonymi aktualizacjami
            : Rozpoznanie potrzeb architektonicznych
    
    section Podstawa architektury
        Centralny projekt stanu
            : Tworzenie zunifikowanych obiektów stanu
            : Wdrażanie kontrolowanych wzorców aktualizacji
            : Ustanowienie zasad niemutowalności
        
        Przewidywalne aktualizacje
            : Opanowanie użycia Object.freeze()
            : Budowanie systemów przyjaznych debugowaniu
            : Tworzenie skalowalnych wzorców
    
    section Opanowanie trwałości
        Integracja z localStorage
            : Obsługa serializacji JSON
            : Wdrażanie automatycznej synchronizacji
            : Tworzenie ciągłości sesji
        
        Równowaga świeżości danych
            : Rozwiązywanie problemów ze starzeniem się danych
            : Budowanie mechanizmów odświeżania
            : Optymalizacja wydajności versus dokładności
    
    section Profesjonalne wzorce
        Systemy gotowe do produkcji
            : Wdrażanie obsługi błędów
            : Tworzenie łatwych do utrzymania architektur
            : Stosowanie najlepszych praktyk branżowych
        
        Zaawansowane możliwości
            : Gotowość do integracji z frameworkami
            : Przygotowanie do złożonych potrzeb stanu
            : Podstawa funkcji czasu rzeczywistego
Loading

🎓 Kamień milowy ukończenia: Pomyślnie zbudowałeś kompletny system zarządzania stanem, używając tych samych zasad, które napędzają Redux, Vuex i inne profesjonalne biblioteki stanu. Te wzorce skalują się od prostych aplikacji do rozwiązań korporacyjnych.

🔄 Następny poziom możliwości:

  • Gotowość do opanowania frameworków zarządzania stanem (Redux, Zustand, Pinia)
  • Przygotowany do implementacji funkcji czasu rzeczywistego z WebSockets
  • Wyposażony w narzędzia do tworzenia offline-first Progressive Web Apps
  • Podstawa do zaawansowanych wzorców, takich jak maszyny stanów i obserwatory

Wyzwanie GitHub Copilot Agent 🚀

Użyj trybu Agenta, aby wykonać następujące wyzwanie:

Opis: Zaimplementuj kompleksowy system zarządzania stanem z funkcjami cofania i ponawiania (undo/redo) dla aplikacji bankowej. To wyzwanie pozwoli Ci poćwiczyć zaawansowane koncepcje zarządzania stanem, w tym śledzenie historii stanu, niezmienne aktualizacje i synchronizację interfejsu użytkownika.

Wskazówka: Stwórz rozszerzony system zarządzania stanem, który zawiera: 1) tablicę historii stanu śledzącą wszystkie poprzednie stany, 2) funkcje cofania i ponawiania umożliwiające powrót do poprzednich stanów, 3) przyciski UI do operacji undo/redo na dashboardzie, 4) limit historii do 10 stanów, by zapobiec problemom z pamięcią, oraz 5) właściwe czyszczenie historii po wylogowaniu użytkownika. Upewnij się, że undo/redo działa z modyfikacjami salda konta i trwa po odświeżeniu przeglądarki.

Dowiedz się więcej o trybie agenta tutaj.

🚀 Wyzwanie: Optymalizacja przechowywania

Twoja implementacja teraz skutecznie obsługuje sesje użytkowników, odświeżanie danych i zarządzanie stanem. Zastanów się jednak, czy nasze obecne podejście optymalnie równoważy efektywność przechowywania z funkcjonalnością.

Jak mistrzowie szachowi, którzy odróżniają ważne figury od zbędnych pionów, skuteczne zarządzanie stanem wymaga identyfikacji danych, które muszą być trwałe, od tych, które powinny być zawsze świeże z serwera.

Analiza optymalizacji:

Oceń swoją obecną implementację localStorage i rozważ te pytania strategiczne:

  • Jakie jest minimum informacji wymagane do utrzymania uwierzytelnienia użytkownika?
  • Które dane zmieniają się na tyle często, że lokalny caching przynosi niewiele korzyści?
  • Jak optymalizacja przechowywania może poprawić wydajność bez pogorszenia doświadczenia użytkownika?

Tego typu analiza architektoniczna wyróżnia doświadczonych programistów, którzy uwzględniają zarówno funkcjonalność, jak i efektywność w swoich rozwiązaniach.

Strategia implementacji:

  • Zidentyfikuj kluczowe dane, które muszą być trwałe (prawdopodobnie tylko identyfikacja użytkownika)
  • Zmodyfikuj implementację localStorage, aby przechowywać tylko krytyczne dane sesyjne
  • Zapewnij, że świeże dane są zawsze ładowane z serwera przy odwiedzinach dashboardu
  • Przetestuj, czy zoptymalizowane podejście utrzymuje takie samo doświadczenie użytkownika

Zaawansowane rozważania:

  • Porównaj kompromisy między przechowywaniem pełnych danych konta a tylko tokenów uwierzytelniania
  • Udokumentuj swoje decyzje i uzasadnienia dla przyszłych członków zespołu

To wyzwanie pomoże Ci myśleć jak profesjonalny programista, który bierze pod uwagę zarówno doświadczenie użytkownika, jak i efektywność aplikacji. Poświęć czas na eksperymenty z różnymi podejściami!

Quiz po wykładzie

Quiz po wykładzie

Zadanie

Zaimplementuj dialog „Dodaj transakcję”

Oto przykładowy efekt po ukończeniu zadania:

Zrzut ekranu pokazujący przykładowy dialog „Dodaj transakcję”


Zastrzeżenie:
Niniejszy dokument został przetłumaczony przy użyciu automatycznej usługi tłumaczeniowej AI Co-op Translator. Mimo że dokładamy starań, aby tłumaczenie było jak najdokładniejsze, prosimy pamiętać, że przekłady automatyczne mogą zawierać błędy lub niedokładności. Oryginalny dokument w języku źródłowym powinien być traktowany jako źródło wiarygodne i ostateczne. W przypadku ważnych informacji zaleca się skorzystanie z profesjonalnego tłumaczenia wykonanego przez człowieka. Nie ponosimy odpowiedzialności za jakiekolwiek nieporozumienia lub błędne interpretacje wynikające z użycia tego tłumaczenia.