Урок 10

Работа с формами

Формы в React бывают контролируемые (controlled) и неконтролируемые (uncontrolled).

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

Значение input хранится в состоянии React. Компонент полностью управляет полем:

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

    return (
        <form>
            <input
                value={name}
                onChange={(e) => setName(e.target.value)}
            />
            <p>Привет, {name}</p>
        </form>
    );
}

Плюсы: можно валидировать на лету, менять значение программно, форматировать ввод.

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

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

    function handleSubmit(e: React.FormEvent) {
        e.preventDefault();
        console.log({ email, password });
    }

    return (
        <form onSubmit={handleSubmit}>
            <input
                type="email"
                value={email}
                onChange={(e) => setEmail(e.target.value)}
                placeholder="Email"
            />
            <input
                type="password"
                value={password}
                onChange={(e) => setPassword(e.target.value)}
                placeholder="Пароль"
            />
            <button type="submit">Войти</button>
        </form>
    );
}

Один обработчик на все поля

Если полей много — используй имя поля:

function Form() {
    const [form, setForm] = useState({
        name: '',
        email: '',
        age: '',
    });

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

    return (
        <form>
            <input name="name" value={form.name} onChange={handleChange} />
            <input name="email" value={form.email} onChange={handleChange} />
            <input name="age" value={form.age} onChange={handleChange} />
        </form>
    );
}

Select

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

Checkbox

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

Radio

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

Textarea

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

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

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

Значение хранится в DOM, доступ через ref:

import { useRef } from 'react';

function Form() {
    const inputRef = useRef<HTMLInputElement>(null);

    function handleSubmit(e: React.FormEvent) {
        e.preventDefault();
        console.log(inputRef.current?.value);
    }

    return (
        <form onSubmit={handleSubmit}>
            <input ref={inputRef} defaultValue="" />
            <button>Отправить</button>
        </form>
    );
}

Используй, когда значение нужно только при отправке, и не требуется валидация на лету.

Валидация

function Form() {
    const [email, setEmail] = useState('');
    const [error, setError] = useState('');

    function handleSubmit(e: React.FormEvent) {
        e.preventDefault();

        if (!email.includes('@')) {
            setError('Некорректный email');
            return;
        }

        setError('');
        // отправка
    }

    return (
        <form onSubmit={handleSubmit}>
            <input value={email} onChange={(e) => setEmail(e.target.value)} />
            {error && <p className="error">{error}</p>}
            <button>Отправить</button>
        </form>
    );
}

Итоги

  • Контролируемые поля — через value и onChange
  • Один обработчик на много полей — через name
  • Checkbox — checked и e.target.checked
  • Submit — onSubmit и preventDefault
  • Неконтролируемые — через ref