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

Kontrolowane vs niekontrolowane komponenty

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 (Controlled Component)

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:

  1. value={stanowy}
    - wartość pola jest powiązana ze stanem
  2. onChange
    - każda zmiana aktualizuje stan przez
    setState
  3. Stan React jest jedynym źródłem prawdy - przeglądarka wyświetla to, co React jej każe

Przepływ danych w kontrolowanym komponencie

Kiedy użytkownik wpisze literę w pole tekstowe:

  1. Przeglądarka wykrywa zmianę i wywołuje
    onChange
  2. Handler aktualizuje stan przez
    setState
  3. React re-renderuje komponent z nową wartością
  4. Pole wyświetla nową wartość ze stanu

To zamknięta pętla - React kontroluje każdy znak wpisany w formularz.

Komponent niekontrolowany (Uncontrolled Component)

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:

  1. ref={referencja}
    - uchwyt do elementu DOM
  2. defaultValue
    zamiast
    value
    - ustawia wartość początkową, ale nie kontroluje dalszych zmian
  3. ref.current.value
    - odczytanie wartości z DOM, gdy jest potrzebna

useRef - dostęp do elementów DOM

Hook

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ść.

Kiedy używać kontrolowanych, a kiedy niekontrolowanych?

| 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).

Walidacja formularza z komponentem kontrolowanym

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.

Podsumowanie

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.

Przejdź do CodeWorlds