Пошук уроків, статей та іншого контенту
Створимо контрольовані форми, синхронізуємо поля зі State та реалізуємо обробку введення й надсилання.
У React форма може працювати у двох основних підходах:
неконтрольований компонент зберігає значення у DOM;
контрольований компонент зберігає значення у стані React.
У контрольованій формі значення кожного поля визначається через state, а кожна зміна поля оновлює цей стан.
<input
value={email}
onChange={(event) => setEmail(event.target.value)}
/>Тут:
value={email} синхронізує поле зі станом;
onChange реагує на введення;
setEmail записує нове значення у стан.
Таким чином, React завжди знає актуальне значення поля.
Розглянемо форму реєстрації на подію. Вона містить ім’я, електронну пошту та вибір формату участі.
import { useState } from "react";
function RegistrationForm() {
const [form, setForm] = useState({
name: "",
email: "",
format: "online",
});
const handleChange = (event) => {
const { name, value } = event.target;
setForm((previousForm) => ({
...previousForm,
[name]: value,
}));
};
const handleSubmit = (event) => {
event.preventDefault();
console.log("Дані форми:", form);
};
return (
<form onSubmit={handleSubmit}>
<label>
Ім’я:
<input
type="text"
name="name"
value={form.name}
onChange={handleChange}
/>
</label>
<label>
Email:
<input
type="email"
name="email"
value={form.email}
onChange={handleChange}
/>
</label>
<label>
Формат:
<select
name="format"
value={form.format}
onChange={handleChange}
>
<option value="online">Онлайн</option>
<option value="offline">Офлайн</option>
</select>
</label>
<button type="submit">Зареєструватися</button>
</form>
);
}
export default RegistrationForm;Для кожного поля важливі три атрибути:
name — ім’я властивості у стані;
value — поточне значення;
onChange — обробник зміни значення.
Завдяки однаковим значенням name і ключам об’єкта form можна використати один обробник для всіх текстових полів і списків.
Стан form є об’єктом:
const [form, setForm] = useState({
name: "",
email: "",
format: "online",
});Під час оновлення не можна змінювати властивість безпосередньо:
// Неправильно
form.name = "Олена";React очікує, що буде створено нове значення стану. Для цього використовують оператор розпилення:
setForm((previousForm) => ({
...previousForm,
[name]: value,
}));Вираз [name] є обчислюваною назвою властивості. Якщо name дорівнює "email", результат буде еквівалентним такому:
setForm((previousForm) => ({
...previousForm,
email: value,
}));Попередні поля зберігаються завдяки ...previousForm, а змінене поле отримує нове значення.
У прикладі використовується:
setForm((previousForm) => ({
...previousForm,
[name]: value,
}));Функція отримує найактуальніше попереднє значення стану. Це безпечний підхід, коли новий стан залежить від попереднього.
Форма надсилається через подію submit:
<form onSubmit={handleSubmit}>Обробник має скасувати стандартну поведінку браузера:
const handleSubmit = (event) => {
event.preventDefault();
console.log(form);
};Без event.preventDefault() браузер зазвичай перезавантажить сторінку. Для React-застосунку це небажано, оскільки стан компонента буде втрачено.
Важливо також указати тип кнопки:
<button type="submit">Надіслати</button>Коли кнопка розташована всередині form і має type="submit", натискання на неї викликає onSubmit форми.
Контрольований підхід дає змогу перевірити значення перед відправленням.
import { useState } from "react";
function ContactForm() {
const [form, setForm] = useState({
name: "",
email: "",
message: "",
});
const [errors, setErrors] = useState({});
const handleChange = (event) => {
const { name, value } = event.target;
setForm((previousForm) => ({
...previousForm,
[name]: value,
}));
setErrors((previousErrors) => ({
...previousErrors,
[name]: "",
}));
};
const validateForm = () => {
const nextErrors = {};
if (!form.name.trim()) {
nextErrors.name = "Введіть ім’я";
}
if (!form.email.trim()) {
nextErrors.email = "Введіть email";
} else if (!form.email.includes("@")) {
nextErrors.email = "Введіть коректний email";
}
if (!form.message.trim()) {
nextErrors.message = "Введіть повідомлення";
}
return nextErrors;
};
const handleSubmit = (event) => {
event.preventDefault();
const nextErrors = validateForm();
if (Object.keys(nextErrors).length > 0) {
setErrors(nextErrors);
return;
}
console.log("Форму успішно надіслано:", form);
};
return (
<form onSubmit={handleSubmit} noValidate>
<div>
<label htmlFor="name">Ім’я</label>
<input
id="name"
name="name"
type="text"
value={form.name}
onChange={handleChange}
/>
{errors.name && <p>{errors.name}</p>}
</div>
<div>
<label htmlFor="email">Email</label>
<input
id="email"
name="email"
type="email"
value={form.email}
onChange={handleChange}
/>
{errors.email && <p>{errors.email}</p>}
</div>
<div>
<label htmlFor="message">Повідомлення</label>
<textarea
id="message"
name="message"
value={form.message}
onChange={handleChange}
/>
{errors.message && <p>{errors.message}</p>}
</div>
<button type="submit">Надіслати</button>
</form>
);
}
export default ContactForm;У цьому прикладі:
form зберігає значення полів.
errors зберігає повідомлення про помилки.
validateForm повертає об’єкт помилок.
Якщо помилки є, надсилання припиняється.
Після виправлення поля його помилка очищається.
Прапорець noValidate вимикає стандартну браузерну валідацію, щоб усі перевірки виконувалися у React. Його використання не є обов’язковим, але воно зручне, коли застосунок має власні повідомлення про помилки.
Для текстових полів значення читають через event.target.value. Для прапорця потрібно використовувати event.target.checked.
import { useState } from "react";
function AgreementForm() {
const [form, setForm] = useState({
email: "",
agreement: false,
});
const handleChange = (event) => {
const { name, type, value, checked } = event.target;
setForm((previousForm) => ({
...previousForm,
[name]: type === "checkbox" ? checked : value,
}));
};
const handleSubmit = (event) => {
event.preventDefault();
if (!form.agreement) {
alert("Потрібно прийняти умови");
return;
}
console.log(form);
};
return (
<form onSubmit={handleSubmit}>
<label>
Email:
<input
type="email"
name="email"
value={form.email}
onChange={handleChange}
/>
</label>
<label>
<input
type="checkbox"
name="agreement"
checked={form.agreement}
onChange={handleChange}
/>
Я приймаю умови
</label>
<button type="submit">Продовжити</button>
</form>
);
}
export default AgreementForm;Для checkbox використовується checked, а не value:
<input
type="checkbox"
checked={form.agreement}
onChange={handleChange}
/>checked має бути логічним значенням: true або false.
Якщо форма має багато полів, зручно використовувати один універсальний обробник:
const handleChange = (event) => {
const { name, value } = event.target;
setForm((previousForm) => ({
...previousForm,
[name]: value,
}));
};Тоді всі поля повинні мати відповідний name:
<input name="firstName" value={form.firstName} onChange={handleChange} />
<input name="lastName" value={form.lastName} onChange={handleChange} />
<input name="phone" value={form.phone} onChange={handleChange} />Стан при цьому може мати такий вигляд:
const [form, setForm] = useState({
firstName: "",
lastName: "",
phone: "",
});Для різних типів полів обробник можна розширити:
const handleChange = (event) => {
const { name, type, value, checked } = event.target;
const nextValue = type === "checkbox" ? checked : value;
setForm((previousForm) => ({
...previousForm,
[name]: nextValue,
}));
};Контрольовані компоненти дають змогу:
отримувати актуальні значення полів у будь-який момент;
виконувати власну валідацію;
показувати помилки поруч із конкретними полями;
блокувати або змінювати кнопку залежно від стану форми;
очищати форму після успішного надсилання;
відправляти на сервер саме ті дані, які зберігаються у state.
Наприклад, після успішного надсилання форму можна очистити:
setForm({
name: "",
email: "",
message: "",
});value<input name="email" onChange={handleChange} />Таке поле не є повністю контрольованим. Для контрольованого компонента потрібно передати і value:
<input
name="email"
value={form.email}
onChange={handleChange}
/>onChange<input value={form.email} />Поле матиме значення, але користувач не зможе його змінити. Якщо React отримує value, потрібно додати onChange або свідомо зробити поле лише для читання.
setForm({
email: event.target.value,
});Після цього властивості name і message буде втрачено. Потрібно зберегти попередні поля:
setForm((previousForm) => ({
...previousForm,
email: event.target.value,
}));value для checkbox<input
type="checkbox"
name="agreement"
value={form.agreement}
onChange={handleChange}
/>Для checkbox потрібно використовувати checked і читати event.target.checked.
event.preventDefault()Якщо не скасувати стандартне надсилання форми, браузер перезавантажить сторінку:
const handleSubmit = (event) => {
event.preventDefault();
};Оновлення стану відбувається не миттєво. Не варто очікувати, що після виклику setForm змінна form уже містить нові дані в тому самому виконанні функції.
setForm(nextForm);
// Тут form може ще містити попереднє значення
console.log(form);Для надсилання форми використовуйте дані, які вже були зібрані в обробнику, або обробляйте актуальний стан під час наступного виклику handleSubmit.
Контрольований компонент отримує значення зі state через value або checked.
Обробник onChange синхронізує введення користувача зі state.
Для кількох полів зручно використовувати один обробник і атрибут name.
Під час оновлення об’єкта стану потрібно зберігати попередні властивості через ...previousForm.
Надсилання форми обробляється через onSubmit.
event.preventDefault() запобігає перезавантаженню сторінки.
Для checkbox використовують checked і event.target.checked.
Контрольований підхід спрощує валідацію, показ помилок і підготовку даних до відправлення.