Неконтролируемые компоненты
Неконтролируемый компонент хранит значение в DOM, а не в React. Доступ к значению — через 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>
);
}
defaultValue задаёт начальное значение, ref даёт доступ к DOM.
Когда использовать
- Простые формы без валидации на лету
- Интеграция со сторонними библиотеками (например, редакторами)
- Загрузка файлов
- Когда нужна производительность (не каждый ввод вызывает ререндер)
Файлы
Неконтролируемый input — стандарт для файлов:
function Upload() {
const fileRef = useRef<HTMLInputElement>(null);
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
const file = fileRef.current?.files?.[0];
if (!file) return;
const formData = new FormData();
formData.append('file', file);
fetch('/api/upload', {
method: 'POST',
body: formData,
});
}
return (
<form onSubmit={handleSubmit}>
<input type="file" ref={fileRef} />
<button>Загрузить</button>
</form>
);
}
Поле type="file" всегда неконтролируемое.
Контролируемый vs неконтролируемый
| Контролируемый | Неконтролируемый |
|---|---|
| Значение в React | Значение в DOM |
| Ре-рендер на каждый ввод | Без ререндера |
| Валидация на лету | Валидация при отправке |
| Программное изменение | Через ref |
| Больше кода | Меньше кода |
Пример: контролируемый
const [email, setEmail] = useState('');
<input value={email} onChange={(e) => setEmail(e.target.value)} />
Пример: неконтролируемый
const emailRef = useRef<HTMLInputElement>(null);
<input ref={emailRef} defaultValue="" />
Форма целиком
Иногда удобно работать с формой как с неконтролируемой:
function Form() {
const formRef = useRef<HTMLFormElement>(null);
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
const data = new FormData(formRef.current!);
const email = data.get('email');
const password = data.get('password');
console.log({ email, password });
}
return (
<form ref={formRef} onSubmit={handleSubmit}>
<input name="email" type="email" />
<input name="password" type="password" />
<button>Войти</button>
</form>
);
}
FormData собирает все поля по имени name.
Смешанный подход
Можно смешивать: часть полей контролируемые, часть — нет.
const [email, setEmail] = useState('');
const passwordRef = useRef<HTMLInputElement>(null);
<input value={email} onChange={(e) => setEmail(e.target.value)} />
<input ref={passwordRef} type="password" />
Но обычно лучше держать единый стиль.
Reset формы
Неконтролируемую форму легко сбросить:
formRef.current?.reset();
Для контролируемой нужно обнулять каждое поле.
Итоги
- Неконтролируемый — значение в DOM
- Доступ через
ref defaultValueдля начального значения- Файлы — всегда неконтролируемые
FormDataсобирает всю форму- Меньше кода, но меньше контроля