Komponent kontrolowany to element formularza, którego wartość żyje w stanie Reacta: przekazujesz value i aktualizujesz stan w onChange, więc to React decyduje, co pokazuje pole. Komponent niekontrolowany trzyma wartość w DOM, jak zwykły HTML: przekazujesz początkowe defaultValue i odczytujesz bieżącą wartość później przez ref albo FormData.
Pisz w obu polach. Akapit podąża za pierwszym przy każdym naciśnięciu klawisza, bo każda zmiana ustawia stan i renderuje ponownie. Drugie pole zmienia się tylko w DOM; React nie renderuje, a jego wartość widzisz dopiero, gdy poprosi o nią "Read both".
Pola kontrolowane
W polu kontrolowanym jedynym źródłem prawdy jest stan Reacta. Przy każdym naciśnięciu klawisza przebieg wygląda tak:
- Użytkownik pisze, a przeglądarka uruchamia zdarzenie input.
- Twój handler
onChangeodczytujee.target.valuei wywołuje setter. - React renderuje ponownie i zapisuje nowy stan z powrotem do
valuepola.
Ponieważ twój kod siedzi w środku tej pętli, możesz robić rzeczy, których pole niekontrolowane nie potrafi: odrzucać albo przekształcać znaki, pokazywać wartość w innym miejscu, wyłączać przycisk, dopóki wartość nie jest poprawna, albo ustawiać wartość z kodu (przycisk „Wyczyść", podpowiedź, którą klika użytkownik).
Spróbuj wpisać litery: nigdy się nie pojawią, bo handler usuwa wszystko, co nie jest cyfrą, zanim ustawi stan. Przycisk Verify pozostaje wyłączony, dopóki nie będzie dokładnie sześciu cyfr.
Pól kontrolowanych używają przykłady na stronie o formularzach i to domyślny wybór w większości kodu w React.
Pola niekontrolowane
Pole niekontrolowane działa jak zwykły HTML. React renderuje je raz z defaultValue (albo defaultChecked dla pól wyboru i przycisków radio), a potem zostawia je w spokoju. Żeby odczytać wartość, trzymaj ref do elementu albo odczytaj wszystkie nazwane pola z formularza przy wysyłaniu przez FormData.
Nie ma tu stanu dla pól ani onChange. Wartości są zbierane dopiero przy wysłaniu formularza. Odznacz pole wyboru i wyślij ponownie: klucz subscribe znika, bo niezaznaczone pole wyboru w ogóle nie jest częścią FormData.
Zmiana defaultValue po pierwszym renderowaniu nic nie robi z polem, bo React stosuje ją tylko przy tworzeniu elementu. Żeby zresetować niekontrolowany formularz, wywołaj e.target.reset() albo nadaj formularzowi nowy key, żeby React utworzył świeże pola.
Pole, w którym nie da się pisać
Jeśli przekażesz value, ale bez onChange, pole jest kontrolowane, ale nic nigdy nie aktualizuje stanu. React po każdym naciśnięciu klawisza zapisuje z powrotem tę samą wartość, więc pole wygląda na zamrożone.
Spróbuj pisać w każdym polu. Pierwsze się nie zmienia. Drugie to poprawka dla pola kontrolowanego i gdy w nim piszesz, pierwsze i ostatnie pole za nim podążają, bo wszystkie trzy czytają ten sam stan. Trzecie przyjmuje tekst, bo jest niekontrolowane. Czwarte celowo mówi, że nie da się go edytować.
W trybie deweloperskim React wypisuje dla pierwszego pola ostrzeżenie: "You provided a value prop to a form field without an onChange handler". Podgląd działa jak build produkcyjny, więc nie zobaczysz tu tego ostrzeżenia, ale zobaczysz je na własnym serwerze deweloperskim.
Zmiana z niekontrolowanego na kontrolowane
React decyduje, czy pole jest kontrolowane, patrząc na value. Jeśli value to undefined albo null, pole jest niekontrolowane. Pole, którego value zaczyna jako undefined, a później staje się tekstem, w trakcie życia przechodzi więc z niekontrolowanego na kontrolowane, a React w trybie deweloperskim ostrzega: "A component is changing an uncontrolled input to be controlled".
Ostrzeżenie wskazuje prawdziwy błąd. Przejście w drugą stronę, z tekstu na undefined, z powrotem robi z pola niekontrolowane, a React przestaje do niego zapisywać.
Wpisz coś w obu polach i naciśnij każdy przycisk Clear. Drugie pole się opróżnia. Pierwsze zachowuje tekst: ustawienie stanu na undefined uczyniło je niekontrolowanym, więc React już nie kontroluje tego, co pokazuje.
Zwykłym źródłem undefined są dane, które jeszcze się nie załadowały (value={user.name}, zanim pojawi się użytkownik), albo pole obiektu, które jeszcze nie istnieje. Dwie poprawki:
const [name, setName] = useState(''); // start with a string
<input value={user?.name ?? ''} /> // or fall back to one
Dla pól wyboru ta sama zasada dotyczy checked: zacznij od false, a nie od undefined.
Pola plików są zawsze niekontrolowane
<input type="file"> nie może być kontrolowany, bo przeglądarki nie pozwalają stronie ustawić jego wartości (strona, która mogłaby wybierać pliki z twojego dysku, byłaby luką bezpieczeństwa). Odczytuj wybrane pliki z e.target.files w onChange albo przez ref przy wysyłaniu formularza.
Wybierz plik albo dwa ze swojego komputera; nic nie jest wysyłane, przykład tylko wypisuje nazwy i rozmiary. Jedyna wartość, jaką możesz ustawić w polu pliku, to pusty tekst, i tak przycisk Clear je resetuje.
Kiedy co wybrać
| Kontrolowane | Niekontrolowane | |
|---|---|---|
| Wartość żyje w | Stanie Reacta | DOM |
| Ustawienie wartości początkowej | value / checked | defaultValue / defaultChecked |
| Odczyt wartości | W każdej chwili, ze stanu | Przez ref albo FormData przy wysyłaniu |
| Renderuje się przy każdym klawiszu | Tak | Nie |
| Zmiana wartości z kodu | Ustawienie stanu | Zapis do DOM przez ref |
| Dobre do | Walidacji na żywo, formatowania, pól zależnych | Prostych formularzy odczytywanych przy wysyłaniu, pól plików, akcji formularzy |
Wybierz pole kontrolowane, gdy ekran musi reagować na wartość w trakcie pisania. Wybierz niekontrolowane, gdy wartości potrzebujesz tylko na końcu, i tak też działają akcje formularzy w React 19: <form action={fn}> przekazuje twojej funkcji FormData i potem resetuje niekontrolowane pola.
Renderowanie przy każdym naciśnięciu klawisza brzmi kosztownie, ale dla normalnego formularza nie jest problemem. Staje się nim dopiero wtedy, gdy razem z polem ponownie renderuje się duże, wolne drzewo; wtedy trzymaj stan blisko pola albo przenieś wolną część do osobnego komponentu.
Te same dwa słowa opisują też twoje własne komponenty. Komponent, który dostaje wartość i onChange od rodzica przez propsy, jest kontrolowany przez rodzica; ten, który trzyma własny stan, jest niekontrolowany. Przenoszenie stanu w górę, żeby komponent stał się kontrolowany, to temat podnoszenia stanu.
Najczęściej zadawane pytania
Co to jest komponent kontrolowany w React?
Element formularza, którego bieżąca wartość pochodzi ze stanu Reacta, ustawiana przez value (albo checked) i aktualizowana przez onChange. Stan Reacta jest jedynym źródłem prawdy, więc zawsze znasz wartość i możesz ją zmienić z kodu.
Co to jest komponent niekontrolowany w React?
Element formularza, który trzyma własną wartość w DOM, jak zwykły HTML. Nadajesz mu wartość początkową przez defaultValue (albo defaultChecked) i odczytujesz bieżącą wartość, gdy jej potrzebujesz, przez ref albo FormData przy wysyłaniu.
Dlaczego nie mogę pisać w polu input w React?
Przekazałeś value bez onChange. Pole jest kontrolowane, więc React po każdym naciśnięciu klawisza wstawia z powrotem wartość ze stanu. Dodaj onChange, który aktualizuje stan, przejdź na defaultValue albo dodaj readOnly, jeśli pole naprawdę nie powinno się zmieniać.
Jak naprawić "A component is changing an uncontrolled input to be controlled"?
value pola zaczęło jako undefined (albo null), a potem stało się tekstem. Upewnij się, że zawsze jest tekstem: zacznij stan od '' albo napisz value={name ?? ''}.
Czy używać pól kontrolowanych, czy niekontrolowanych?
Używaj pól kontrolowanych, gdy UI reaguje na wartość w trakcie pisania: walidacja, formatowanie, wyłączony przycisk, pola zależne od siebie. Używaj niekontrolowanych, gdy wartości potrzebujesz tylko przy wysyłaniu, i dla pól plików, które zawsze są niekontrolowane.