Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Obsługa błędów w formularzach

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.

Wzorce walidacji formularzy

W React możemy wyroznic dwa główne podejścia do walidacji formularzy:

1. Walidacja po stronie klienta (client-side)

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}

2. Walidacja po stronie serwera (server-side)

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};

Wyswietlanie błędów na poziomie pol (field-level errors)

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!

Obsługa błędów serwera w formularzach

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}

try-catch w handleSubmit

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};

UX best practices dla komunikatow błędów

Dobre komunikaty błędów w formularzach powinny byc:

  1. Konkretne -- "Haslo musi miec min. 8 znakow" zamiast "Nieprawidlowe haslo"
  2. Pomocne -- powiedz uzytkownikowi JAK naprawic blad
  3. Widoczne -- wyswietlaj blad blisko pola, którego dotyczy
  4. Delikatne -- nie wyswietlaj wszystkich bledow na raz przed pierwszym submit
  5. Dostepne -- uzywaj atrybutow
    aria-invalid
    i
    aria-describedby
1<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.

Przejdź do CodeWorlds