Zarządzanie stanem w React: zachowaj prostotę w aplikacjach generowanych
Zarządzanie stanem w React uproszczone: oddziel stan serwera od stanu klienta, przestrzegaj kilku zasad i szybko rozpoznawaj wczesne oznaki rosnącej złożoności.

Co idzie nie tak ze stanem w prawdziwych aplikacjach React
Stan to dowolne dane, które mogą się zmieniać podczas działania aplikacji. To obejmuje to, co widzisz (modal jest otwarty), co edytujesz (szkic formularza) oraz dane, które pobierasz (lista projektów). Problem w tym, że wszystko to nazywa się "stan", mimo że zachowuje się bardzo różnie.
Większość zaplątanych aplikacji psuje się w ten sam sposób: zbyt wiele rodzajów stanu jest wymieszanych w tym samym miejscu. Komponent trzyma dane serwera, flagi UI, szkice formularzy i wartości pochodne, a potem próbuje je synchronizować efektami. Wkrótce nie potrafisz odpowiedzieć na proste pytania: "skąd pochodzi ta wartość?" albo "co ją aktualizuje?" bez przeszukiwania kilku plików.
Generowane aplikacje React wpadają w to szybciej, bo łatwo zaakceptować pierwszą działającą wersję. Dodajesz nowy ekran, kopiujesz wzorzec, łatasz błąd kolejnym useEffect, i nagle masz dwa źródła prawdy. Jeśli generator lub zespół zmienia kierunek w trakcie (lokalny stan tu, globalny store tam), kod zbiera wzorce zamiast budować jeden spójny.
Cel jest nudny: mniej rodzajów stanu i mniej miejsc do sprawdzenia. Gdy jest jedno oczywiste miejsce dla danych serwera i jedno dla stanu tylko UI, błędy stają się mniejsze, a zmiany przestają być ryzykowne.
"Keep it boring" oznacza trzymanie się kilku zasad:
- Nie odwzorowuj danych serwera w stanie klienta bez jasnego powodu.
- Nie promotuj lokalnego stanu UI do globalnego store'a tylko dlatego, że "może kiedyś się przyda".
- Nie pozwól, by wartości pochodne stały się własnym stanem, chyba że ich obliczanie jest naprawdę kosztowne.
Konkret: jeśli lista użytkowników pochodzi z backendu, traktuj ją jako server state i pobieraj tam, gdzie jest potrzebna. Jeśli selectedUserId istnieje tylko po to, by napędzać panel szczegółów, trzymaj go jako lokalny stan UI blisko tego panelu. Mieszanie tych dwóch rzeczy to początek narastającej złożoności.
Stan serwera vs stan klienta prostym językiem
Większość problemów ze stanem w React zaczyna się od jednego pomieszania: traktowania danych serwera jak stan UI. Oddziel je wcześnie, a zarządzanie stanem będzie spokojne, nawet gdy aplikacja urośnie.
Stan serwera należy do backendu: użytkownicy, zamówienia, zadania, uprawnienia, ceny, feature flagi. Może się zmieniać bez udziału twojej aplikacji (inna karta może je zaktualizować, admin edytuje, uruchamia się job, dane wygasają). Ponieważ jest współdzielony i zmienny, potrzebujesz fetchowania, cache'owania, refetchowania i obsługi błędów.
Stan klienta to to, czym interesuje się tylko twoje UI w danym momencie: który modal jest otwarty, która karta jest wybrana, przełącznik filtra, kolejność sortowania, zwinięty sidebar, szkic wyszukiwarki. Jeśli zamkniesz kartę, można to stracić.
Szybki test: "Czy mógłbym odświeżyć stronę i odbudować to z serwera?"
- Jeśli tak — prawdopodobnie to server state.
- Jeśli nie — prawdopodobnie to client state.
Jest też stan pochodny (derived), który ratuje Cię przed tworzeniem dodatkowego stanu. To wartość, którą możesz obliczyć z innych wartości, więc jej nie przechowujesz. Filtrowane listy, sumy, isFormValid i "pokaż pusty stan" zwykle należą tutaj.
Przykład: pobierasz listę projektów (server state). Wybrany filtr i flaga dialogu "Nowy projekt" to client state. Widoczna lista po filtrowaniu to stan pochodny. Jeśli przechowasz widoczną listę osobno, zacznie się rozjeżdżać i będziesz gonić błędy "dlaczego jest przestarzała?".
To rozdzielenie pomaga, gdy narzędzie takie jak Koder.ai generuje ekrany szybko: trzymaj dane backendu w jednej warstwie fetchującej, decyzje UI blisko komponentów i unikaj przechowywania wartości obliczonych.
Nudny zestaw zasad, który zapobiega większości problemów
Stan staje się bolesny, gdy jedna część danych ma dwóch właścicieli. Najszybszy sposób, by utrzymać prostotę, to zdecydować kto za co odpowiada i się tego trzymać.
- Traktuj dane z API jako coś, co pobierasz i cache'ujesz, a nie jako coś, czym ręcznie zarządzasz w stanie komponentu.
- Nie klonuj pobranych danych do lokalnego stanu "na zapas". Kopie powodują dryft.
- Trzymaj stan klienta blisko miejsca użycia. Przenieś go wyżej tylko wtedy, gdy naprawdę różne części drzewa go potrzebują.
- Przechowuj ID i małe flagi, nie całych obiektów. Odraderuj obiekt z cache'u przy renderze.
Przykład: pobierasz listę użytkowników i pokazujesz szczegóły po wybraniu jednego. Powszechny błąd to trzymanie pełnego wybranego obiektu w stanie. Przechowuj selectedUserId zamiast tego. Trzymaj listę w cache'u serwera. Widok szczegółów wyszukuje użytkownika po ID, więc refetchy aktualizują UI bez dodatkowego kodu sync.
W generowanych aplikacjach React łatwo zaakceptować "pomocny" wygenerowany stan, który duplikuje dane serwera. Gdy widzisz kod robiący fetch -> setState -> edytuj -> refetch, zatrzymaj się. To często znak, że budujesz drugą bazę danych w przeglądarce.
Jak obsługiwać server state bez nadmiernego myślenia
Server state to wszystko, co żyje na backendzie: listy, strony szczegółów, wyniki wyszukiwania, uprawnienia, liczniki. Nudne podejście to wybrać jedno narzędzie do tego i się go trzymać. W wielu aplikacjach React TanStack Query wystarcza.
Cel jest prosty: komponenty proszą o dane, pokazują loading i error, i nie przejmują się ile fetchów dzieje się pod spodem. To ważne w generowanych aplikacjach, bo małe niespójności mnożą się szybko, gdy dodaje się nowe ekrany.
Traktuj query keys jak system nazewnictwa, nie dodatek. Utrzymuj je spójne: stabilne tablice kluczy, uwzględniaj tylko wejścia zmieniające wynik (filtry, strona, sort), i preferuj kilka przewidywalnych kształtów zamiast wielu jednorazowych. Wiele zespołów umieszcza budowanie kluczy w małych helperach, żeby każdy ekran używał tych samych reguł.
Do zapisów używaj mutation z wyraźną obsługą sukcesu. Mutacja powinna odpowiadać na dwa pytania: co się zmieniło i co UI powinno zrobić dalej?
Przykład: tworzysz nowe zadanie. Po sukcesie albo unieważnij zapytanie listy zadań (żeby się przeładowało raz), albo zrób ukierunkowaną aktualizację cache'u (dodaj nowe zadanie do zbuforowanej listy). Wybierz jedną metodę na funkcję i trzymaj się jej.
Jeśli czujesz pokusę dodawać refetchy w wielu miejscach "na wszelki wypadek", wybierz jedną nudną akcję zamiast tego:
- Unieważnij dokładny query key, który stał się nieaktualny.
- Zaktualizuj cache dla dokładnego zapytania, które się zmieniło.
- Nawiguj do ekranu, który już fetchuje właściwe zapytanie.
Wzorce client state, które pozostają małe
Client state to rzeczy należące do przeglądarki: flaga otwartego sidebaru, wybrany wiersz, tekst filtra, szkic przed zapisem. Trzymaj go blisko miejsca użycia, a zwykle pozostanie on do opanowania.
Zacznij od małego: useState w najbliższym komponencie. Przy generowaniu ekranów (np. z Koder.ai) kuszące jest wrzucenie wszystkiego do globalnego store'a "na wypadek". Tak kończy się store, którego nikt nie rozumie.
Prosta reguła promocji
Przenieś stan w górę tylko wtedy, gdy potrafisz nazwać problem współdzielenia.
- Domyślnie trzymaj stan UI lokalnie.
- Podnieś go do najbliższego wspólnego rodzica, gdy siostrzane komponenty go potrzebują.
- Użyj małego współdzielonego store'a tylko wtedy, gdy wiele tras lub odległych komponentów potrzebuje tej samej wartości jednocześnie.
Przykład: tabela z panelem szczegółów może trzymać selectedRowId w komponencie tabeli. Jeśli toolbar w innej części strony też go potrzebuje, podnieś go do komponentu strony. Jeśli osobna trasa (np. bulk edit) też go potrzebuje, wtedy mały store ma sens.
Kształty store'ów, które pozostają czytelne
Jeśli używasz store'a (Zustand lub podobny), trzymaj go skoncentrowanego na jednym zadaniu. Przechowuj "co" (wybrane ID, filtry), nie "wyniki" (posortowane listy), które możesz wyprowadzić.
Gdy store zaczyna rosnąć, zapytaj: czy to wciąż jedna funkcja? Jeśli odpowiedź brzmi "tak na pół", podziel go teraz, zanim kolejna funkcja zamieni go w kulę stanu, której boisz się dotykać.
Formularze, szkice i tymczasowy stan UI
Błędy w formularzach często wynikają z mieszania trzech rzeczy: tego, co użytkownik wpisuje, tego, co serwer zapisał, i tego, co UI pokazuje.
Dla nudnego zarządzania stanem traktuj formularz jako client state do momentu wysłania. Dane serwera to ostatnia zapisana wersja. Formularz to szkic. Nie edytuj obiektu serwera „na miejscu”. Skopiuj wartości do szkicu, pozwól użytkownikowi swobodnie zmieniać, a potem zatwierdź i refetchuj (lub zaktualizuj cache) po sukcesie.
Zdecyduj wcześnie, co powinno przetrwać nawigację użytkownika. Ta jedna decyzja zapobiega wielu niespodziankom. Na przykład inline edit i otwarte dropdowny zwykle resetują się, podczas gdy długi szkic kreatora lub nie wysłana wiadomość mogą być zapisywane. Trzymaj trwałość po reload tylko gdy użytkownicy jej oczekują (np. koszyk zakupowy).
Trzymaj reguły walidacji w jednym miejscu. Jeśli rozrzucisz reguły po inputach, handlerach submit i helperach, skończysz z niespójnymi błędami. Preferuj jedną schemę (albo jedną funkcję validate()), a UI niech decyduje, kiedy pokazywać błędy (on change, on blur lub on submit).
Przykład: generujesz ekran Edytuj Profil w Koder.ai. Załaduj zapisany profil jako server state. Stwórz szkic lokalny dla pól formularza. Pokaż "niesave'owane zmiany" porównując szkic z wersją zapisaną. Jeśli użytkownik anuluje, porzuć szkic i pokaż wersję z serwera. Jeśli zapisze, wyślij szkic, a potem zastąp wersję zapisaną odpowiedzią z serwera.
Krok po kroku: uprość zabałaganione ustawienie stanu
W miarę jak generowana aplikacja React rośnie, często te same dane pojawiają się w trzech miejscach: stanie komponentu, globalnym store i cache'u. Naprawa zwykle nie wymaga nowej biblioteki. To wybór jednego domu dla każdego kawałka stanu.
Przepływ porządkowy, który działa w większości aplikacji:
- Zrób inwentaryzację stanu. Wypisz co masz (listy, wybrany element, filtry, modale, szkice) i oznacz każdy jako server lub client.
- Usuń duplikaty. Usuń stan jak
filteredUsers, jeśli możesz go obliczyć zusers+filter. PreferujselectedUserIdzamiast zdublowanegoselectedUser. - Umieść fetching w jednej warstwie. Użyj jednego podejścia do server-state, żeby cache, refetch i invalidation miały jedną książkę reguł.
- Dodaj mały client store tylko tam, gdzie naprawdę się opłaci (wspólne potrzeby między stronami, jak szkic kreatora).
- Zablokuj reguły nazewnictwa, żeby bałagan się nie wkradł z powrotem.
Przykład: wygenerowana przez Koder.ai aplikacja CRUD często zaczyna się od useEffect fetch + globalna kopia listy. Po scentralizowaniu server state lista pochodzi z jednego zapytania, a "odśwież" staje się invalidacją zamiast ręcznej synchronizacji.
Dla nazewnictwa trzymaj je spójne i nudne:
- Queries:
users.list,users.detail(id) - Client state:
ui.isCreateModalOpen,filters.userSearch - Akcje:
openCreateModal(),setUserSearch(value) - Zapisy na serwerze:
users.create,users.update,users.delete
Cel: jedno źródło prawdy dla każdej rzeczy i wyraźne granice między stanem serwera a stanem klienta.
Wczesne sygnały, że złożoność stanu zaraz wybuchnie
Problemy ze stanem zaczynają się mało, potem pewnego dnia zmieniasz pole i trzy części UI nie zgadzają się co do "prawdziwej" wartości.
Najjaśniejszym sygnałem ostrzegawczym są zdublowane dane: ten sam użytkownik lub koszyk żyje w komponencie, globalnym store i cache'u zapytań. Każda kopia aktualizuje się w innym czasie i dodajesz więcej kodu tylko po to, by je zrównać.
Inny sygnał to kod synchronizujący: efekty, które przesyłają stan tam i z powrotem. Wzorce typu "gdy query się zmienia, aktualizuj store" i "gdy store się zmienia, refetch" działają, dopóki przypadek brzegowy nie wywoła stale'ujących wartości lub pętli.
Kilka szybkich czerwonych flag:
- Nie potrafisz wyjaśnić "skąd ta wartość pochodzi" i "kto za nią odpowiada" w jednym zdaniu.
- Twój store lub reducer importuje klientów API lub zna kody statusów HTTP.
- Każdy nowy ekran dodaje współdzielone flagi typu
needsRefresh,didInit,isSaving, których nikt nie usuwa. - Piszesz efekty głównie po to, by mirrorować jeden stan w drugi.
- Naprawiając bug musisz aktualizować to samo pole w kilku miejscach.
Przykład: generujesz dashboard w Koder.ai i dodajesz modal Edit Profile. Jeśli dane profilu są w query cache, skopiowane do globalnego store i zdublowane w lokalnym stanie formularza, masz teraz trzy źródła prawdy. Przy pierwszym dodaniu background refetch lub optimistic update pokażą niespójności.
Gdy widzisz te znaki, nudny ruch to wybrać jednego właściciela dla każdego kawałka danych i usunąć mirror'y.
Typowe pułapki i jak ich unikać
Przechowywanie rzeczy "na zapas" to jedna z najszybszych dróg do bolesnego stanu, szczególnie w generowanych aplikacjach.
Kopiowanie odpowiedzi API do globalnego store'a to częsta pułapka. Jeśli dane pochodzą z serwera (listy, szczegóły, profil użytkownika), nie kopiuj ich domyślnie do store'a klienta. Wybierz jedno miejsce dla danych serwera (zazwyczaj query cache). Używaj client store dla wartości UI, o których serwer nie wie.
Przechowywanie wartości pochodnych to kolejna pułapka. Liczniki, filtrowane listy, sumy, canSubmit i isEmpty zwykle powinny być obliczane z wejść. Jeśli wydajność stanie się realnym problemem, memoizuj później, ale nie zaczynaj od przechowywania wyniku.
Pojedynczy mega-store na wszystko (auth, modale, toasty, filtry, szkice, flagi onboardingowe) staje się śmietnikiem. Dziel według granic funkcji. Jeśli stan jest używany tylko przez jeden ekran, trzymaj go lokalnie.
Context jest świetny dla wartości stabilnych (theme, current user id, locale). Dla szybko zmieniających się wartości może powodować szerokie rerendery. Używaj Context do przekazywania konfiguracji, a stan często zmieniający się trzymaj w stanie komponentu (lub małym store).
Na koniec: unikaj niespójnego nazewnictwa. Prawie identyczne query keys i pola store'a tworzą subtelne duplikacje. Wybierz prosty standard i się go trzymaj.
Szybka lista kontrolna zanim dodasz więcej stanu
Gdy masz ochotę dodać "jeszcze jedną" zmienną stanu, zrób szybki test własności.
Po pierwsze: czy potrafisz wskazać jedno miejsce, gdzie zachodzi fetching i cache'owanie serwera (jedno narzędzie, jeden zestaw query keys)? Jeśli te same dane są fetchowane w wielu komponentach i też kopiowane do store'a, już płacisz odsetki.
Po drugie: czy ta wartość jest potrzebna tylko w jednym ekranie (np. "panel filtra otwarty")? Jeśli tak, nie powinna być globalna.
Po trzecie: czy możesz przechowywać ID zamiast duplikować obiekt? Przechowuj selectedUserId i odczytuj użytkownika z cache lub listy.
Po czwarte: czy to wartość pochodna? Jeśli da się ją obliczyć z istniejącego stanu, nie przechowuj jej.
Wreszcie, zrób test trasowania na minutę. Jeśli kolega nie potrafi odpowiedzieć "skąd ta wartość pochodzi?" (prop, lokalny stan, cache serwera, URL, store) w mniej niż minutę, napraw własność zanim dodasz więcej stanu.
Przykład: aplikacja CRUD, która pozostaje nudna w miarę wzrostu
Wyobraź sobie wygenerowane admin UI (np. z promptu w Koder.ai) z trzema ekranami: lista klientów, strona szczegółów klienta i formularz edycji.
Stan pozostaje spokojny, gdy ma oczywiste domy:
- Server state: lista klientów, rekord klienta, zapytania save/delete.
- Client state: filtry listy, wybrany wiersz, szkic edycji.
Strony listy i szczegółów czytają server state z query cache. Gdy zapisujesz, nie przechowujesz klientów ponownie w globalnym store. Wysyłasz mutację, a potem pozwalasz cache'owi odświeżyć się lub aktualizujesz go.
Dla ekranu edycji trzymaj szkic lokalnie. Inicjalizuj go z pobranego klienta, ale traktuj jako oddzielny, gdy użytkownik zaczyna pisać. Dzięki temu widok szczegółów może się bezpiecznie odświeżyć bez nadpisywania niedokończonych zmian.
Nudna optymistyczna aktualizacja
UI optymistyczne to miejsce, gdzie zespoły często duplikują wszystko. Zazwyczaj nie trzeba tego robić.
Gdy użytkownik klika Zapisz, zaktualizuj tylko zbuforowany rekord klienta i odpowiadający element listy, a potem wycofaj zmianę, jeśli żądanie się nie powiedzie. Trzymaj szkic w formularzu dopóki zapis nie powiedzie się. Jeśli nie uda się, pokaż błąd i zostaw szkic, żeby użytkownik mógł spróbować ponownie.
Gdy druga funkcja potrzebuje tego samego stanu
Załóżmy, że dodajesz bulk edit i też potrzebuje wybranych wierszy. Zanim stworzysz nowy store, zapytaj: czy ten stan powinien przetrwać nawigację i reload?
- Jeśli tak, umieść go w URL (wybrane ID, filtry).
- Jeśli nie, trzymaj w komponencie strony.
- Jeśli wiele odległych komponentów naprawdę potrzebuje go jednocześnie (toolbar + tabela + stopka), wprowadź mały współdzielony store tylko dla tego client state.
Następne kroki: utrzymuj reguły spójne w generowanych aplikacjach
Generowane ekrany mogą mnożyć się szybko i to świetnie, dopóki każdy nowy ekran nie wnosi własnych decyzji odnośnie stanu.
Zapisz krótką notkę zespołową w repo: co liczy się jako server state, co jako client state i które narzędzie za co odpowiada. Trzymaj to na tyle krótkie, by ludzie rzeczywiście tego przestrzegali.
Dodaj mały zwyczaj w PR: oznacz każdy nowy kawałek stanu jako server lub client. Jeśli to server state, zapytaj "gdzie się ładuje, jak jest cache'owane i co to unieważnia?" Jeśli to client state, zapytaj "kto za to odpowiada i kiedy się resetuje?"
Jeśli używasz Koder.ai (koder.ai), Planning Mode może pomóc uzgodnić granice stanu zanim wygenerujesz nowe ekrany. Migawka i rollback dają bezpieczny sposób na eksperymentowanie, gdy refaktor stanu idzie nie tak.
Wybierz jedną funkcję (np. edycja profilu), zastosuj reguły od początku do końca i niech to będzie przykład, który wszyscy kopiują.
Często zadawane pytania
What’s the simplest way to untangle messy React state?
Zacznij od oznaczenia każdego kawałka stanu jako server, client (UI) lub derived.
- Server: pobierane dane, np. users, tasks, permissions.
- Client: wybory UI, np. „modal open”, wybrana zakładka, tekst filtra.
- Derived: wartości, które możesz obliczyć (filtrowana lista, sumy,
isValid).
Gdy je oznaczysz, upewnij się, że każdy element ma jedno oczywiste miejsce odpowiedzialności (query cache, lokalny stan komponentu, URL lub mały store).
How do I tell if something is server state or client state?
Użyj prostego testu: „Czy mogę odświeżyć stronę i zbudować to ponownie z serwera?”
- Jeśli tak, traktuj to jako server state (fetch/cache/refetch).
- Jeśli nie, traktuj to jako client state (lokalny stan UI, ew. store).
Przykład: lista projektów to server state; selectedRowId to client state.
Why is copying API data into local or global state such a big problem?
Bo tworzy to dwa źródła prawdy.
Jeśli pobierasz users, a potem kopiujesz je do useState lub globalnego store'a, musisz je teraz utrzymywać w synchronizacji przy:
- tła refetchach
- aktualizacjach z innych zakładek/użytkowników
- częściowych aktualizacjach po mutacjach
Zasada: czytaj dane serwera z jednego miejsca (np. query cache) i twórz lokalny stan tylko dla UI lub szkiców.
When is it okay to store derived values instead of computing them?
Przechowuj pochodne wartości tylko wtedy, gdy naprawdę nie da się ich tanio obliczyć.
Zwykle obliczasz je z istniejących wejść:
visibleUsers = users.filter(...)total = items.reduce(...)canSubmit = isValid && !isSaving
Jeśli problemem jest wydajność (zmierzone), preferuj useMemo lub lepsze struktury danych, zanim dodasz więcej przechowywanego stanu, który może się zdezaktualizować.
What’s a “boring” way to handle server state in React?
Domyślnie: użyj narzędzia do server-state (często TanStack Query), aby komponenty mogły po prostu „poprosić o dane” i obsłużyć loading/error.
Praktyczne podstawy:
- stabilne, spójne query keys (uwzględniaj tylko wejścia zmieniające wynik),
- do zapisów używaj mutations i wybierz jedną strategię dla danej funkcji:
- unieważnij dokładne zapytanie listy/szczegółu, lub
- zaktualizuj cache w sposób ukierunkowany.
Unikaj rozsypywania refetch() po kodzie „na wszelki wypadek.”
When should I move UI state into a global store?
Trzymaj to lokalnie dopóki nie potrafisz nazwać rzeczywistej potrzeby współdzielenia.
Reguła promocji:
- Domyślnie lokalny stan komponentu.
- Podnieś go do najbliższego wspólnego rodzica, gdy rodzeństwo go potrzebuje.
- Użyj małego współdzielonego store'a tylko wtedy, gdy wiele odległych komponentów/tras potrzebuje tej samej wartości jednocześnie.
Dzięki temu globalny store nie stanie się składowiskiem losowych flag UI.
Should I store the selected object or just its ID?
Przechowuj ID i małe flagi, nie całe obiekty serwera.
Przykład:
- ✅
selectedUserId - ❌
selectedUser(skopiowany obiekt)
Następnie renderuj szczegóły, odczytując użytkownika z cache lub listy. Dzięki temu background refetch i aktualizacje będą działać poprawnie bez dodatkowych efektów synchronizacji.
What’s the cleanest way to manage forms and drafts?
Traktuj formularz jako szkic (client state) dopóki nie zatwierdzisz.
Wzorzec praktyczny:
- Pobierz zapisany rekord jako server state.
- Zainicjalizuj lokalny szkic z tego rekordu.
- Pozwól użytkownikowi dowolnie edytować szkic.
- Przy zapisie wyślij szkic, a potem odśwież/zaktualizuj cache serwera.
- Przy anulowaniu porzuć szkic i pokaż wersję z serwera.
To zapobiega przypadkowemu edytowaniu danych serwera „in place” i walce z refetchami.
What are early warning signs that state complexity is about to explode?
Typowe czerwone flagi:
- Te same dane istnieją w komponencie, globalnym store i query cache.
- Efekty, które odzwierciedlają stan tam i z powrotem ("gdy query się zmienia, ustaw store", "gdy store się zmienia, refetch").
- Wspólne flagi jak
needsRefresh,didInit,isSaving, które się kumulują. - Nie potrafisz w jednym zdaniu powiedzieć „skąd pochodzi ta wartość”.
Naprawa zwykle nie wymaga nowej biblioteki — trzeba usunąć kopie i wybrać jednego właściciela dla każdej wartości.
How do I keep state “boring” when using a generator like Koder.ai?
Generowane ekrany szybko popadają w mieszane wzorce. Proste zabezpieczenie: ustal własność stanów:
- Server state: zawsze przez ten sam warstwę fetch/cache.
- Client UI state: domyślnie lokalny, store tylko gdy potrzebny.
- Derived values: obliczane, nie przechowywane.
Jeśli używasz Koder.ai, skorzystaj z Planning Mode, aby ustalić granice stanu przed generowaniem nowych ekranów i używaj snapshotów/rollbacku przy eksperymentach.