Урок 02

Контролируемые компоненты

Контролируемый компонент — это элемент формы, значение которого хранится в состоянии React. React полностью управляет полем.

Базовый пример

function NameInput() {
    const [name, setName] = useState('');

    return (
        <input
            value={name}
            onChange={(e) => setName(e.target.value)}
        />
    );
}

Что происходит:

  1. Пользователь вводит символ
  2. Срабатывает onChange
  3. setName обновляет состояние
  4. React перерисовывает компонент
  5. value устанавливается в новое значение

Зачем контролировать

  • Валидация на лету — можно проверить значение сразу
  • Форматирование — например, маска телефона
  • Программное изменение — очистить поле, установить значение
  • Зависимые поля — одно поле зависит от другого

Одно поле

const [email, setEmail] = useState('');

<input
    type="email"
    value={email}
    onChange={(e) => setEmail(e.target.value)}
/>

Несколько полей

Отдельные состояния:

const [email, setEmail] = useState('');
const [password, setPassword] = useState('');

<input value={email} onChange={(e) => setEmail(e.target.value)} />
<input value={password} onChange={(e) => setPassword(e.target.value)} />

Один обработчик через name:

const [form, setForm] = useState({ email: '', password: '' });

function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    const { name, value } = e.target;
    setForm((f) => ({ ...f, [name]: value }));
}

<input name="email" value={form.email} onChange={handleChange} />
<input name="password" value={form.password} onChange={handleChange} />

Select

const [role, setRole] = useState('user');

<select value={role} onChange={(e) => setRole(e.target.value)}>
    <option value="user">Пользователь</option>
    <option value="admin">Администратор</option>
    <option value="moderator">Модератор</option>
</select>

В отличие от HTML, selected на опции не используется — значение задаётся на select.

Textarea

const [bio, setBio] = useState('');

<textarea value={bio} onChange={(e) => setBio(e.target.value)} />

В React textarea тоже контролируется через value, а не через дочерний текст.

Checkbox

const [agreed, setAgreed] = useState(false);

<input
    type="checkbox"
    checked={agreed}
    onChange={(e) => setAgreed(e.target.checked)}
/>

Для checkbox используется checked и e.target.checked, а не value.

Radio

const [gender, setGender] = useState('male');

<label>
    <input
        type="radio"
        value="male"
        checked={gender === 'male'}
        onChange={(e) => setGender(e.target.value)}
    />
    Мужской
</label>

<label>
    <input
        type="radio"
        value="female"
        checked={gender === 'female'}
        onChange={(e) => setGender(e.target.value)}
    />
    Женский
</label>

Множественный select

const [tags, setTags] = useState<string[]>([]);

<select
    multiple
    value={tags}
    onChange={(e) => setTags(
        Array.from(e.target.selectedOptions, (o) => o.value)
    )}
>
    <option value="js">JavaScript</option>
    <option value="ts">TypeScript</option>
    <option value="react">React</option>
</select>

Массив чекбоксов

const [selected, setSelected] = useState<number[]>([]);

function toggle(id: number) {
    setSelected((s) =>
        s.includes(id) ? s.filter((i) => i !== id) : [...s, id]
    );
}

{items.map((item) => (
    <label key={item.id}>
        <input
            type="checkbox"
            checked={selected.includes(item.id)}
            onChange={() => toggle(item.id)}
        />
        {item.name}
    </label>
))}

Предупреждение React

Если поставить value без onChange — React выдаст предупреждение. Поле будет только для чтения.

<input value="только чтение" /> // предупреждение
<input value="только чтение" readOnly /> // ОК

Итоги

  • Контролируемое поле — value + onChange
  • Checkbox — checked + e.target.checked
  • Select — value на select, не на option
  • Textarea — value, не дочерний текст
  • Radio — checked с условием
  • Один обработчик на много полей — через name