Menu

Hook useState w React: przykłady i częste błędy

useState daje komponentowi wartość, która przetrwa ponowne renderowanie, i setter, który przerysowuje ekran, gdy wartość się zmienia. Poznaj składnię, funkcje aktualizujące, obiekty i tablice w stanie i dowiedz się, dlaczego wartość wygląda na nieaktualną zaraz po jej ustawieniu.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

useState to hook, który pozwala komponentowi zapamiętać coś między renderowaniami: licznik, tekst w polu, informację, czy menu jest otwarte. Za każdym razem, gdy wywołujesz jego setter, React renderuje komponent ponownie z nową wartością.

Kliknij przycisk, a potem zmień w edytorze useState(0) na useState(10). Podgląd się przebuduje, a licznik zacznie od 10.

Składnia

const [value, setValue] = useState(initialValue);
  • initialValue jest używane tylko przy pierwszym renderowaniu. Przy każdym kolejnym React je ignoruje i oddaje wartość, którą zapamiętał.
  • value to bieżąca wartość dla tego renderowania.
  • setValue zastępuje wartość i planuje nowe renderowanie.

Nazwy wybierasz sam. useState zwraca dwuelementową tablicę, a nawiasy kwadratowe ją rozpakowują, więc [isOpen, setIsOpen] i [email, setEmail] to ten sam wzorzec.

Wywołuj useState na najwyższym poziomie komponentu, nigdy w pętli, warunku ani zagnieżdżonej funkcji. React dopasowuje każde wywołanie do zapamiętanej wartości po kolejności wywołań, więc ta kolejność musi być taka sama przy każdym renderowaniu (zobacz zasady hooków).

Kilka kawałków stanu

Komponent może wywołać useState tyle razy, ile potrzebuje. Każde wywołanie to osobna, niezależna wartość.

Stan to migawka

Setter nie zmienia zmiennej, którą trzymasz. Mówi Reactowi, jaka ma być wartość w następnym renderowaniu. Dopóki to renderowanie nie nastąpi, bieżące wciąż widzi starą wartość.

Otwórz konsolę pod podglądem i kliknij raz. Handler wypisuje 0, starą wartość, a potem komponent wypisuje nowe renderowanie z 1. Wszystko jest w porządku: count jest stałą przez całe renderowanie, a następne renderowanie dostaje nową.

Funkcje aktualizujące

Z powodu migawki trzykrotne wywołanie settera z count + 1 dodaje jeden, a nie trzy: każde wywołanie liczy 0 + 1. Przekaż zamiast tego funkcję, a React poda każdemu wywołaniu wynik poprzedniego.

Używaj formy z funkcją aktualizującą zawsze, gdy następna wartość zależy od poprzedniej, a aktualizacja może nie wykonywać się bezpośrednio z najnowszego renderowania: kilka ustawień w jednym handlerze, callback setInterval albo efekt.

Obiekty i tablice w stanie

React decyduje, czy renderować ponownie, porównując starą i nową wartość przez Object.is. Jeśli zmienisz obiekt w miejscu i przekażesz z powrotem ten sam obiekt, porównanie mówi, że nic się nie zmieniło, a ekran zostaje taki, jaki był. Zawsze przekazuj nowy obiekt albo nową tablicę.

{ ...user, age: user.age + 1 } kopiuje każde pole i nadpisuje jedno. [...tags, 'code'] dodaje element, a filter usuwa jeden, i oba zwracają nową tablicę. Strona o aktualizowaniu tablic i obiektów omawia zagnieżdżone obiekty, edycję jednego elementu listy i wstawianie w środek.

Leniwy stan początkowy

Argument useState jest obliczany przy każdym renderowaniu, choć React używa go tylko za pierwszym razem. Gdy jego wyliczenie jest kosztowne (odczyt localStorage, parsowanie dużego tekstu), przekaż funkcję. React wywoła ją raz, przy pierwszym renderowaniu.

// Runs readSavedNotes() on every render, then throws the result away
const [notes, setNotes] = useState(readSavedNotes());

// Runs it once
const [notes, setNotes] = useState(readSavedNotes);
const [notes, setNotes] = useState(() => readSavedNotes());

Resetowanie stanu przez key

Stan należy do pozycji komponentu w drzewie. Jeśli w tym samym miejscu wyrenderujesz inne key, React potraktuje to jako nowy komponent, a jego stan zacznie od nowa. To najczystszy sposób na zresetowanie formularza, gdy zmienia się to, co on edytuje.

Wpisz wiadomość, a potem zmień odbiorcę: szkic się wyczyści. Usuń key={to} i spróbuj ponownie, a tekst zostanie, bo React widzi teraz ten sam Draft w tym samym miejscu.

Częste błędy

Wypisywanie stanu zaraz po jego ustawieniu. Widzisz starą wartość, jak pokazano powyżej. Wypisuj w ciele komponentu albo najpierw wylicz następną wartość do zmiennej i jej użyj.

Mutowanie stanu. user.age++; setUser(user) przekazuje ten sam obiekt, więc nic się nie renderuje. Najpierw zrób kopię.

Wywoływanie settera podczas renderowania. <button onClick={setCount(count + 1)}> wywołuje setCount w trakcie renderowania, co renderuje ponownie, co znowu go wywołuje. React przerywa tę pętlę komunikatem "Too many re-renders". Przekaż funkcję: onClick={() => setCount(count + 1)}.

Przechowywanie wartości, które można wyliczyć. Jeśli fullName to zawsze first + ' ' + last, wyliczaj je podczas renderowania, zamiast trzymać trzeci kawałek stanu, który może się rozjechać.

Najczęściej zadawane pytania

Co zwraca useState?

Tablicę z dokładnie dwoma elementami: bieżącą wartością i funkcją, która ją zastępuje. Sam je nazywasz przez destrukturyzację tablicy, zgodnie z konwencją [thing, setThing].

Dlaczego mój stan nie aktualizuje się od razu?

Wywołanie settera nie zmienia zmiennej, którą już masz. Prosi React o ponowne wyrenderowanie komponentu, a nowa wartość pojawia się w tym następnym renderowaniu. W bieżącym handlerze zdarzenia w zasięgu jest wciąż stara wartość, więc console.log(count) zaraz po setCount(count + 1) wypisuje starą liczbę.

Kiedy przekazywać do settera funkcję?

Gdy nowa wartość zależy od starej i ustawiasz ją więcej niż raz albo z wnętrza timera czy efektu. setCount(c => c + 1) zawsze zaczyna od najnowszej wartości, a setCount(count + 1) od wartości, którą widziało to renderowanie.

Czy useState może przechowywać obiekt albo tablicę?

Tak, ale traktuj je jako tylko do odczytu. Zbuduj nowy obiekt albo tablicę i przekaż je do settera (setUser({ ...user, name }), setItems([...items, item])). Zmiana istniejącego obiektu w miejscu nie wywołuje renderowania.

Czym różni się useState od useReducer?

Oba przechowują stan. useState pasuje do kilku niezależnych wartości. useReducer przenosi logikę aktualizacji do jednej funkcji, która dostaje akcję, co czyta się lepiej, gdy wiele zdarzeń zmienia ten sam stan w powiązany sposób.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ