Как обработать форму с неуправляемыми компонентами?
Компоненты
Неуправляемая форма хранит значения в DOM; React читает их в момент отправки — через FormData или refs.
Способ 1: FormData (самый чистый)
const OrderForm = () => {
const handleSubmit = (e) => {
e.preventDefault();
const data = Object.fromEntries(new FormData(e.currentTarget));
api.send(data); // { name: '...', email: '...' }
e.currentTarget.reset();
};
return (
<form onSubmit={handleSubmit}>
<input name="name" defaultValue="" required />
<input name="email" type="email" required />
<button>Отправить</button>
</form>
);
};Не нужны ни state, ни refs: значения берутся по атрибуту name. Работает нативная валидация (required, type=email), автозаполнение браузера, Enter в поле отправляет форму.
Способ 2: refs на поля
const nameRef = useRef(null);
<input ref={nameRef} defaultValue="Гость" />
// при отправке: nameRef.current.valueУдобно, когда полей мало или нужно императивно фокусировать/сбрасывать конкретное поле.
Валидация
- Нативная (required, pattern, minLength) +
form.checkValidity()/input.setCustomValidity(). - Ручная в handleSubmit: собрать данные, проверить схемой (Zod) и показать ошибки из state.
- Для серьёзных форм — React Hook Form: те же неуправляемые поля (
register), но с валидацией, ошибками и reset из коробки.
Нюансы
defaultValue/defaultCheckedвместо value/checked; менять значение декларативно нельзя.- Смена defaultValue после монтирования на поле не влияет — нужен
keyили императивная запись. <input type="file">— только так; файл берётся изe.target.files.
Коротко для собеседования
Значения читаются на submit: new FormData(e.currentTarget) (поля по name, работает нативная валидация и reset) или через refs у отдельных полей; начальные значения — defaultValue. Валидация — нативная или схемой в обработчике; для сложных форм — React Hook Form, который построен на этом же подходе.