Na stacji kosmicznej każdy system działa w jednym z dwóch trybów: albo centrum dowodzenia ma pełną kontrolę nad parametrami (tryb ręczny), albo system działa autonomicznie, a centrum tylko odczytuje dane w razie potrzeby (tryb automatyczny). W React formularze i pola wejściowe działają analogicznie - mogą być kontrolowane przez stan komponentu lub niekontrolowane, gdzie przeglądarka zarządza ich wartością.
Komponent kontrolowany to taki, w którym React jest "jedynym źródłem prawdy" dla wartości pola formularza. Każda zmiana wartości przechodzi przez stan komponentu:
1import React, { useState } from 'react';
2
3function PilotLogin() {
4 const [pilotName, setPilotName] = useState('');
5 const [accessCode, setAccessCode] = useState('');
6
7 const handleSubmit = (e) => {
8 e.preventDefault();
9 console.log(`Logowanie pilota: ${pilotName}, kod: ${accessCode}`);
10 };
11
12 return (
13 <form onSubmit={handleSubmit}>
14 <label>
15 Imię pilota:
16 <input
17 type="text"
18 value={pilotName}
19 onChange={(e) => setPilotName(e.target.value)}
20 />
21 </label>
22 <label>
23 Kod dostępu:
24 <input
25 type="password"
26 value={accessCode}
27 onChange={(e) => setAccessCode(e.target.value)}
28 />
29 </label>
30 <button type="submit">Zaloguj</button>
31 </form>
32 );
33}Kluczowe elementy kontrolowanego komponentu:
value={stanowy} - wartość pola jest powiązana ze stanemonChange - każda zmiana aktualizuje stan przez setStateKiedy użytkownik wpisze literę w pole tekstowe:
onChangesetStateTo zamknięta pętla - React kontroluje każdy znak wpisany w formularz.
W komponencie niekontrolowanym to przeglądarka (DOM) zarządza wartością pola. React nie śledzi każdej zmiany - zamiast tego odczytuje wartość dopiero wtedy, gdy jej potrzebuje (np. przy wysłaniu formularza). Do odczytu wartości z DOM używamy ref (referencji):
1import React, { useRef } from 'react';
2
3function QuickSearch() {
4 const searchInputRef = useRef(null);
5
6 const handleSearch = (e) => {
7 e.preventDefault();
8 // Odczytujemy wartość bezpośrednio z DOM
9 const query = searchInputRef.current.value;
10 console.log(`Szukam: ${query}`);
11 };
12
13 return (
14 <form onSubmit={handleSearch}>
15 <input
16 type="text"
17 ref={searchInputRef}
18 defaultValue=""
19 placeholder="Szukaj w bazie danych stacji..."
20 />
21 <button type="submit">Szukaj</button>
22 </form>
23 );
24}Kluczowe elementy niekontrolowanego komponentu:
ref={referencja} - uchwyt do elementu DOMdefaultValue zamiast value - ustawia wartość początkową, ale nie kontroluje dalszych zmianref.current.value - odczytanie wartości z DOM, gdy jest potrzebnaHook
useRef tworzy obiekt z właściwością current, która wskazuje na element DOM po zamontowaniu komponentu:1import React, { useRef } from 'react';
2
3function FocusInput() {
4 const inputRef = useRef(null);
5
6 const handleClick = () => {
7 // Bezpośredni dostęp do elementu DOM
8 inputRef.current.focus();
9 inputRef.current.style.borderColor = '#4caf50';
10 };
11
12 return (
13 <div>
14 <input ref={inputRef} type="text" placeholder="Kliknij przycisk..." />
15 <button onClick={handleClick}>Ustaw fokus</button>
16 </div>
17 );
18}useRef ma ważną cechę: zmiana ref.current nie powoduje re-renderu komponentu. To czyni go idealnym do przechowywania referencji do elementów DOM bez wpływu na wydajność.| Scenariusz | Kontrolowany | Niekontrolowany | |------------|:---:|:---:| | Walidacja w czasie rzeczywistym (każdy znak) | tak | - | | Formatowanie danych na bieżąco (np. numer karty) | tak | - | | Warunkowe wyłączanie przycisku submit | tak | - | | Dynamiczne pola zależne od siebie | tak | - | | Prosty formularz "wyślij i zapomnij" | - | tak | | Integracja z kodem zewnętrznym (non-React) | - | tak | | Maksymalna wydajność (brak re-renderów przy każdym znaku) | - | tak |
Zasada ogólna: domyślnie używaj komponentów kontrolowanych. Niekontrolowane stosuj tylko wtedy, gdy masz konkretny powód (wydajność, integracja z biblioteką zewnętrzną, plik upload).
Wielką zaletą komponentów kontrolowanych jest możliwość walidacji danych w czasie rzeczywistym:
1function CrewRegistration() {
2 const [name, setName] = useState('');
3 const [age, setAge] = useState('');
4 const [errors, setErrors] = useState({});
5
6 const validate = () => {
7 const newErrors = {};
8 if (name.length < 2) newErrors.name = 'Imię musi mieć min. 2 znaki';
9 if (!age || age < 18 || age > 65) newErrors.age = 'Wiek: 18-65 lat';
10 return newErrors;
11 };
12
13 const handleSubmit = (e) => {
14 e.preventDefault();
15 const newErrors = validate();
16 if (Object.keys(newErrors).length === 0) {
17 console.log(`Rejestracja: ${name}, wiek: ${age}`);
18 } else {
19 setErrors(newErrors);
20 }
21 };
22
23 return (
24 <form onSubmit={handleSubmit}>
25 <div>
26 <input
27 value={name}
28 onChange={(e) => setName(e.target.value)}
29 placeholder="Imię członka załogi"
30 />
31 {errors.name && <span style={{color: 'red'}}>{errors.name}</span>}
32 </div>
33 <div>
34 <input
35 type="number"
36 value={age}
37 onChange={(e) => setAge(e.target.value)}
38 placeholder="Wiek"
39 />
40 {errors.age && <span style={{color: 'red'}}>{errors.age}</span>}
41 </div>
42 <button type="submit">Zarejestruj</button>
43 </form>
44 );
45}W komponencie kontrolowanym możemy na bieżąco sprawdzać dane, wyświetlać komunikaty o błędach i warunkować dostępność przycisku submit - to niemożliwe w komponencie niekontrolowanym bez dodatkowego kodu.
Kontrolowane komponenty dają pełną władzę nad formularzem - React śledzi każdą zmianę wartości, co umożliwia walidację w czasie rzeczywistym, formatowanie i dynamiczne zachowanie. Niekontrolowane komponenty są prostsze - przeglądarka zarządza wartością, a React odczytuje ją przez ref tylko gdy trzeba. W większości przypadków kontrolowane komponenty są lepszym wyborem, ale niekontrolowane mają swoje miejsce w prostych formularzach i integracjach.