Wyobraź sobie, że twoja stacja kosmiczna ma panel rejestracji nowych czlonkow zalogi. Każde pole formularza to jak czujnik na statku -- jeśli dane są niepoprawne, system musi natychmiast powiadomic operatora zanim sekwencja startowa zostanie uruchomiona. Obsługa błędów w formularzach to kluczowy element UX każdej aplikacji React.
W React możemy wyroznic dwa główne podejścia do walidacji formularzy:
Walidacja wykonywana w przegladarce, zanim dane zostaną wysłane na serwer. Jest szybka i daje natychmiastowy feedback.
1function RegistrationForm() {
2 const [email, setEmail] = useState('');
3 const [errors, setErrors] = useState({});
4
5 const validateEmail = (value) => {
6 if (!value) return 'Email jest wymagany';
7 if (!value.includes('@')) return 'Nieprawidlowy format email';
8 return null;
9 };
10
11 const handleChange = (e) => {
12 const value = e.target.value;
13 setEmail(value);
14 const error = validateEmail(value);
15 setErrors(prev => ({ ...prev, email: error }));
16 };
17
18 return (
19 <div>
20 <input value={email} onChange={handleChange} />
21 {errors.email && <p className="error">{errors.email}</p>}
22 </div>
23 );
24}Serwer sprawdza dane po wyslaniu formularza i zwraca błędy. Jest niezbedna dla bezpieczenstwa -- walidacja kliencka może zostac pominięta.
1const handleSubmit = async (formData) => {
2 try {
3 const response = await fetch('/api/register', {
4 method: 'POST',
5 body: JSON.stringify(formData),
6 });
7 const result = await response.json();
8
9 if (!response.ok) {
10 // Serwer zwrocil bledy walidacji
11 setErrors(result.errors);
12 }
13 } catch (error) {
14 setErrors({ general: 'Blad polaczenia z serwerem' });
15 }
16};Każde pole formularza powinno miec swój własny komunikat błędu, wyswietlany bezposrednio pod polem. To jak indywidualne alarmy dla każdego systemu na statku kosmicznym.
1function CrewRegistration() {
2 const [formData, setFormData] = useState({
3 name: '', callsign: '', rank: ''
4 });
5 const [errors, setErrors] = useState({});
6 const [touched, setTouched] = useState({});
7
8 const validate = (field, value) => {
9 switch (field) {
10 case 'name':
11 if (!value.trim()) return 'Imie jest wymagane';
12 if (value.length < 2) return 'Imie musi miec min. 2 znaki';
13 return null;
14 case 'callsign':
15 if (!value.trim()) return 'Znak wywoławczy jest wymagany';
16 if (!/^[A-Z0-9-]+$/.test(value)) return 'Tylko wielkie litery, cyfry i myslniki';
17 return null;
18 case 'rank':
19 if (!value) return 'Wybierz range';
20 return null;
21 default:
22 return null;
23 }
24 };
25
26 const handleBlur = (field) => {
27 setTouched(prev => ({ ...prev, [field]: true }));
28 const error = validate(field, formData[field]);
29 setErrors(prev => ({ ...prev, [field]: error }));
30 };
31
32 const handleChange = (field, value) => {
33 setFormData(prev => ({ ...prev, [field]: value }));
34 // Waliduj tylko jezeli pole bylo juz dotykane
35 if (touched[field]) {
36 const error = validate(field, value);
37 setErrors(prev => ({ ...prev, [field]: error }));
38 }
39 };
40
41 return (
42 <form>
43 <div>
44 <label>Imie</label>
45 <input
46 value={formData.name}
47 onChange={(e) => handleChange('name', e.target.value)}
48 onBlur={() => handleBlur('name')}
49 className={errors.name ? 'input-error' : ''}
50 />
51 {touched.name && errors.name && (
52 <p className="field-error">{errors.name}</p>
53 )}
54 </div>
55 {/* Analogicznie dla pozostalych pol */}
56 </form>
57 );
58}Zwroc uwage na wzorzec
touched -- błędy wyswietlamy dopiero po tym, jak uzytkownik opuscil pole (onBlur). Nie chcemy straszyc pilota alarmami zanim jeszcze skonczyl wpisywac dane!Serwer może zwrocic błędy, których nie da sie przewidziec po stronie klienta -- np. "ten email jest juz zajety" albo "przekroczono limit rejestracj".
1function SubmitWithServerErrors() {
2 const [errors, setErrors] = useState({});
3 const [serverError, setServerError] = useState(null);
4
5 const handleSubmit = async (e) => {
6 e.preventDefault();
7 setServerError(null);
8
9 try {
10 const response = await fetch('/api/crew/register', {
11 method: 'POST',
12 headers: { 'Content-Type': 'application/json' },
13 body: JSON.stringify(formData),
14 });
15
16 const data = await response.json();
17
18 if (!response.ok) {
19 if (data.fieldErrors) {
20 // Bledy konkretnych pol (np. "email juz istnieje")
21 setErrors(data.fieldErrors);
22 }
23 if (data.message) {
24 // Ogolny blad serwera
25 setServerError(data.message);
26 }
27 return;
28 }
29
30 // Sukces - przekierowanie lub komunikat
31 } catch (error) {
32 setServerError('Nie udalo sie polaczyc ze stacja bazowa');
33 }
34 };
35
36 return (
37 <form onSubmit={handleSubmit}>
38 {serverError && (
39 <div className="server-error-banner">{serverError}</div>
40 )}
41 {/* Pola formularza z field-level errors */}
42 </form>
43 );
44}Każdy handler wysylania formularza powinien byc opakowany w
try-catch. Bez tego blad sieciowy lub nieoczekiwany blad serwera spowoduje nieobsluzony wyjątek.1const handleSubmit = async (e) => {
2 e.preventDefault();
3 setIsSubmitting(true);
4 setErrors({});
5
6 try {
7 // 1. Walidacja lokalna
8 const validationErrors = validateAllFields(formData);
9 if (Object.keys(validationErrors).length > 0) {
10 setErrors(validationErrors);
11 return;
12 }
13
14 // 2. Wyslanie do serwera
15 const response = await submitToServer(formData);
16
17 // 3. Obsluga odpowiedzi serwera
18 if (!response.ok) {
19 const errorData = await response.json();
20 throw new Error(errorData.message || 'Blad serwera');
21 }
22
23 // 4. Sukces
24 setSuccess(true);
25 } catch (error) {
26 // 5. Obsluga bledow
27 setErrors({ submit: error.message });
28 } finally {
29 // 6. Zawsze wykonane - wylacz loader
30 setIsSubmitting(false);
31 }
32};Dobre komunikaty błędów w formularzach powinny byc:
aria-invalid i aria-describedby1<div className="form-field">
2 <label htmlFor="password">Haslo</label>
3 <input
4 id="password"
5 type="password"
6 aria-invalid={!!errors.password}
7 aria-describedby={errors.password ? 'password-error' : undefined}
8 />
9 {errors.password && (
10 <p id="password-error" className="field-error" role="alert">
11 {errors.password}
12 </p>
13 )}
14</div>Pamietaj -- formularz to jak panel sterowania statku kosmicznego. Kazdy blad musi byc komunikowany jasno i precyzyjnie, aby pilot (uzytkownik) wiedzial dokladnie, co poprawic. Nigdy nie pokazuj tajemniczych kodow bledow -- zawsze tlumacz je na jezyk zrozumialy dla czlowieka.