Форми та обробка форм у React
Обробка Форм в React
React надає два підходи для обробки форм: контрольовані компоненти (React керує станом форми) та неконтрольовані компоненти (DOM керує станом форми). React 19 також вводить дії форм.
Контрольовані Компоненти
Стан React є єдиним джерелом істини для значень введення:
tsx
function LoginForm() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
login({ email, password });
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
/>
<input
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
/>
<button type="submit">Увійти</button>
</form>
);
}Неконтрольовані Компоненти (useRef)
DOM зберігає дані; React читає їх за потреби:
tsx
function SearchForm() {
const inputRef = useRef<HTMLInputElement>(null);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
console.log(inputRef.current?.value); // Читання з DOM
};
return (
<form onSubmit={handleSubmit}>
<input ref={inputRef} type="text" defaultValue="" />
<button type="submit">Пошук</button>
</form>
);
}Складна Форма з useReducer
tsx
type FormState = {
name: string;
email: string;
role: string;
errors: Record<string, string>;
};
type FormAction =
| { type: "SET_FIELD"; field: string; value: string }
| { type: "SET_ERROR"; field: string; error: string }
| { type: "RESET" };
function formReducer(state: FormState, action: FormAction): FormState {
switch (action.type) {
case "SET_FIELD":
return { ...state, [action.field]: action.value };
case "SET_ERROR":
return { ...state, errors: { ...state.errors, [action.field]: action.error } };
case "RESET":
return { name: "", email: "", role: "", errors: {} };
}
}
function RegistrationForm() {
const [state, dispatch] = useReducer(formReducer, {
name: "", email: "", role: "", errors: {}
});
return (
<form>
<input
value={state.name}
onChange={e => dispatch({ type: "SET_FIELD", field: "name", value: e.target.value })}
/>
{state.errors.name && <span>{state.errors.name}</span>}
{/* ... більше полів */}
</form>
);
}React Hook Form (Популярна Бібліотека)
tsx
import { useForm } from "react-hook-form";
interface FormData {
email: string;
password: string;
age: number;
}
function SignUpForm() {
const {
register,
handleSubmit,
formState: { errors, isSubmitting }
} = useForm<FormData>();
const onSubmit = async (data: FormData) => {
await createUser(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input
{...register("email", {
required: "Email є обов'язковим",
pattern: { value: /^[^@]+@[^@]+$/, message: "Неправильний email" }
})}
/>
{errors.email && <span>{errors.email.message}</span>}
<input
type="password"
{...register("password", { required: true, minLength: 8 })}
/>
{errors.password && <span>Мінімум 8 символів</span>}
<input
type="number"
{...register("age", { min: 18, max: 120 })}
/>
<button disabled={isSubmitting}>
{isSubmitting ? "Відправка..." : "Зареєструватися"}
</button>
</form>
);
}Контрольовані vs Неконтрольовані
| Особливість | Контрольовані | Неконтрольовані |
|---|---|---|
| Значення зберігається в | Стані React | DOM |
| Валідація | При кожній зміні | При відправці |
| Перерендери | При кожному натисканні клавіші | Мінімальні |
| Використання | Складні форми, жива валідація | Простi форми, введення файлів |
| Бібліотека | React Hook Form, Formik | Вбудована форма, useRef |
Шаблон Валідації Форм
tsx
function validateEmail(email: string): string | null {
if (!email) return "Email є обов'язковим";
if (!/^[^@]+@[^@]+\.[^@]+$/.test(email)) return "Неправильний email";
return null;
}
function Form() {
const [email, setEmail] = useState("");
const [error, setError] = useState<string | null>(null);
const [touched, setTouched] = useState(false);
const handleBlur = () => {
setTouched(true);
setError(validateEmail(email));
};
return (
<div>
<input
value={email}
onChange={e => { setEmail(e.target.value); if (touched) setError(validateEmail(e.target.value)); }}
onBlur={handleBlur}
/>
{touched && error && <span className="error">{error}</span>}
</div>
);
}Важливо:
Використовуйте контрольовані компоненти, коли вам потрібна валідація в реальному часі, умовна логіка або синхронізований стан. Використовуйте неконтрольовані компоненти для простих форм або введення файлів. Для виробничих додатків надавайте перевагу React Hook Form — це мінімізує перерендери та забезпечує відмінну валідацію. Завжди також проводьте валідацію на сервері.
Коротка відповідь
Для співбесідиPremium
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.