useFormState — это хук React для работы с формами и серверными действиями:
import { useFormState } from 'react-dom';
function ContactForm() {
const [state, formAction] = useFormState(submitForm, null);
return (
<form action={formAction}>
<input name="email" type="email" />
<button type="submit">Отправить</button>
{state?.error && <p>{state.error}</p>}
</form>
);
}useFormState — это современный хук для работы с формами в React! Он упрощает интеграцию с серверными действиями и управление состоянием форм. 🚀
import { useFormState } from 'react-dom';
const [state, formAction] = useFormState(action, initialState);// action - серверная функция
async function submitForm(prevState, formData) {
// Обработка данных формы
const email = formData.get('email');
try {
await sendEmail(email);
return { success: true, message: 'Отправлено!' };
} catch (error) {
return { error: 'Ошибка отправки' };
}
}
// initialState - начальное состояние
const initialState = { message: null, error: null };
function MyForm() {
const [state, formAction] = useFormState(submitForm, initialState);
return (
<form action={formAction}>
<input name="email" required />
<button type="submit">Отправить</button>
{state?.success && <p>✅ {state.message}</p>}
{state?.error && <p>❌ {state.error}</p>}
</form>
);
}async function contactAction(prevState, formData) {
const name = formData.get('name');
const email = formData.get('email');
const message = formData.get('message');
// Валидация
if (!name || !email || !message) {
return { error: 'Заполните все поля' };
}
try {
await sendContactForm({ name, email, message });
return { success: true, message: 'Сообщение отправлено!' };
} catch (error) {
return { error: 'Ошибка сервера' };
}
}
function ContactForm() {
const [state, formAction] = useFormState(contactAction, null);
return (
<form action={formAction}>
<input name="name" placeholder="Имя" required />
<input name="email" type="email" placeholder="Email" required />
<textarea name="message" placeholder="Сообщение" required />
<button type="submit">Отправить</button>
{state?.success && (
<div className="success">✅ {state.message}</div>
)}
{state?.error && (
<div className="error">❌ {state.error}</div>
)}
</form>
);
}async function registerAction(prevState, formData) {
const username = formData.get('username');
const password = formData.get('password');
// Валидация
if (password.length < 6) {
return { error: 'Пароль должен быть минимум 6 символов' };
}
try {
const user = await createUser({ username, password });
return { success: true, user };
} catch (error) {
return { error: 'Пользователь уже существует' };
}
}
function RegisterForm() {
const [state, formAction] = useFormState(registerAction, null);
return (
<form action={formAction}>
<input name="username" placeholder="Логин" required />
<input name="password" type="password" placeholder="Пароль" required />
<button type="submit">Зарегистрироваться</button>
{state?.success && (
<p>Добро пожаловать, {state.user.username}!</p>
)}
{state?.error && <p className="error">{state.error}</p>}
</form>
);
}async function uploadAction(prevState, formData) {
const file = formData.get('file');
if (!file || file.size === 0) {
return { error: 'Выберите файл' };
}
if (file.size > 5 * 1024 * 1024) {
return { error: 'Файл слишком большой (макс. 5MB)' };
}
try {
const url = await uploadFile(file);
return { success: true, url };
} catch (error) {
return { error: 'Ошибка загрузки файла' };
}
}
function UploadForm() {
const [state, formAction] = useFormState(uploadAction, null);
return (
<form action={formAction}>
<input name="file" type="file" accept="image/*" required />
<button type="submit">Загрузить</button>
{state?.success && (
<div>
<p>✅ Файл загружен!</p>
<img src={state.url} alt="Uploaded" width="200" />
</div>
)}
{state?.error && <p className="error">{state.error}</p>}
</form>
);
}import { useFormState } from 'react-dom';
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Отправка...' : 'Отправить'}
</button>
);
}
function MyForm() {
const [state, formAction] = useFormState(submitAction, null);
return (
<form action={formAction}>
<input name="data" required />
<SubmitButton />
{state?.error && <p>{state.error}</p>}
</form>
);
}async function validateAction(prevState, formData) {
const email = formData.get('email');
const age = formData.get('age');
const errors = {};
// Валидация email
if (!email.includes('@')) {
errors.email = 'Неверный формат email';
}
// Валидация возраста
if (age < 18) {
errors.age = 'Возраст должен быть 18+';
}
if (Object.keys(errors).length > 0) {
return { errors };
}
// Сохранение данных
await saveUser({ email, age });
return { success: true };
}
function ValidationForm() {
const [state, formAction] = useFormState(validateAction, null);
return (
<form action={formAction}>
<div>
<input name="email" type="email" placeholder="Email" />
{state?.errors?.email && (
<span className="error">{state.errors.email}</span>
)}
</div>
<div>
<input name="age" type="number" placeholder="Возраст" />
{state?.errors?.age && (
<span className="error">{state.errors.age}</span>
)}
</div>
<button type="submit">Сохранить</button>
{state?.success && <p>✅ Данные сохранены!</p>}
</form>
);
}import { useOptimistic } from 'react';
async function addCommentAction(prevState, formData) {
const comment = formData.get('comment');
// Имитация задержки сервера
await new Promise(resolve => setTimeout(resolve, 1000));
const newComment = {
id: Date.now(),
text: comment,
author: 'Пользователь'
};
return { success: true, comment: newComment };
}
function CommentForm({ comments, onAddComment }) {
const [state, formAction] = useFormState(addCommentAction, null);
const [optimisticComments, addOptimisticComment] = useOptimistic(
comments,
(state, newComment) => [...state, newComment]
);
return (
<div>
<form
action={async (formData) => {
const comment = formData.get('comment');
addOptimisticComment({
id: Date.now(),
text: comment,
author: 'Пользователь'
});
const result = await formAction(formData);
if (result?.success) {
onAddComment(result.comment);
}
}}
>
<textarea name="comment" placeholder="Ваш комментарий" />
<button type="submit">Добавить</button>
</form>
<div>
{optimisticComments.map(comment => (
<div key={comment.id}>{comment.text}</div>
))}
</div>
</div>
);
}// ✅ Правильно
async function goodAction(prevState, formData) {
try {
// Валидация
const data = validateFormData(formData);
// Обработка
const result = await processData(data);
return { success: true, data: result };
} catch (error) {
return { error: error.message };
}
}❌ Неправильно:
// Забыли обработать ошибки
async function badAction(prevState, formData) {
const result = await api.call(formData); // Может упасть
return result;
}✅ Правильно:
// Правильная обработка ошибок
async function goodAction(prevState, formData) {
try {
const result = await api.call(formData);
return { success: true, data: result };
} catch (error) {
return { error: 'Что-то пошло не так' };
}
}useFormState — мощный инструмент для работы с формами:
Используйте его для создания современных и отзывчивых форм! 🎯