Работа с FormData
FormData — встроенный браузерный API для сбора данных формы. Удобен, когда полей много или есть файлы.
Базовое использование
function Form() {
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
const data = new FormData(e.currentTarget);
console.log(data.get('email'));
console.log(data.get('password'));
}
return (
<form onSubmit={handleSubmit}>
<input name="email" type="email" />
<input name="password" type="password" />
<button>Войти</button>
</form>
);
}
e.currentTarget — форма. Поля собираются по имени name.
Методы
const data = new FormData();
data.append('key', 'value');
data.append('file', fileInput.files[0]);
data.get('key'); // значение
data.getAll('key'); // все значения
data.has('key'); // есть ли ключ
data.delete('key');
data.set('key', 'new'); // заменяет
Отправка на сервер
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
const data = new FormData(e.currentTarget);
const res = await fetch('/api/posts', {
method: 'POST',
body: data,
});
}
Не устанавливай Content-Type вручную — fetch сам подставит multipart/form-data с boundary.
Файлы
<form onSubmit={handleSubmit}>
<input name="title" />
<input type="file" name="cover" />
<button>Загрузить</button>
</form>
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
const data = new FormData(e.currentTarget);
await fetch('/api/upload', { method: 'POST', body: data });
}
Несколько файлов
<input type="file" name="files" multiple />
const files = data.getAll('files');
JSON vs FormData
JSON — для API с Content-Type: application/json. Удобно, когда данные простые.
FormData — для форм с файлами и когда сервер ожидает multipart/form-data.
Преобразование в объект
const data = new FormData(e.currentTarget);
const obj = Object.fromEntries(data);
Object.fromEntries работает, если у каждого поля одно значение.
С файлами и множественными значениями:
const data = new FormData(e.currentTarget);
const obj: Record<string, FormDataEntryValue | FormDataEntryValue[]> = {};
for (const [key, value] of data.entries()) {
if (key in obj) {
const existing = obj[key];
obj[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];
} else {
obj[key] = value;
}
}
Отправка с токеном
await fetch('/api/posts', {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
},
body: data,
});
Content-Type не трогай.
Проверка размера файла
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
const data = new FormData(e.currentTarget);
const file = data.get('cover') as File;
if (file && file.size > 5 * 1024 * 1024) {
alert('Файл больше 5 МБ');
return;
}
// отправка
}
Итоги
FormDataсобирает поля поnameget,getAll,append,set,delete,has- Для файлов —
type="file" - Не устанавливай
Content-Typeвручную Object.fromEntriesдля преобразования в объект- Хорошо работает с
fetch