Відкрийте для себе потужну та сучасну валідацію форм у React. Цей вичерпний посібник досліджує experimental_useForm_Status hook, server actions та парадигму валідації статусу для створення надійних та продуктивних форм.
Опанування валідації форм за допомогою React `experimental_useFormStatus`
Форми є основою веб-взаємодії. Від простої підписки на розсилку до складної багатоетапної фінансової програми, вони є основним каналом, через який користувачі спілкуються з нашими програмами. Проте, протягом багатьох років, керування станом форми в React було джерелом складності, шаблонів коду та втоми від залежностей. Ми жонглювали контрольованими компонентами, боролися з бібліотеками керування станом і писали незліченні обробники `onChange`, все це в гонитві за безперебійним та інтуїтивно зрозумілим користувацьким досвідом.
Команда React переосмислює цей фундаментальний аспект веб-розробки, що призвело до впровадження нової, потужної парадигми, зосередженої навколо React Server Actions. Ця нова модель, побудована на принципах прогресивного покращення, має на меті спростити обробку форм, переміщуючи логіку ближче до того місця, де вона має бути - часто, на сервер. В основі цієї клієнтської революції лежать два нові експериментальні хуки: `useFormState` та зірка нашої сьогоднішньої дискусії, `experimental_useFormStatus`.
Цей вичерпний посібник проведе вас у глибоке занурення в хук `experimental_useFormStatus`. Ми не просто розглянемо його синтаксис; ми дослідимо ментальну модель, яку він уможливлює: Логіка валідації на основі статусу. Ви дізнаєтесь, як цей хук відокремлює UI від стану форми, спрощує керування станами очікування та працює у співпраці з Server Actions для створення надійних, доступних і високопродуктивних форм, які працюють навіть до завантаження JavaScript. Підготуйтеся переосмислити все, що ви знали про створення форм у React.
Зміна парадигми: Еволюція React Forms
Щоб повністю оцінити інновації, які привносить `useFormStatus`, ми повинні спочатку зрозуміти шлях керування формами в екосистемі React. Цей контекст підкреслює проблеми, які цей новий підхід елегантно вирішує.
Стара гвардія: Контрольовані компоненти та сторонні бібліотеки
Протягом багатьох років стандартним підходом до форм у React був патерн контрольованого компонента. Це передбачає:
- Використання змінної стану React (наприклад, з `useState`) для зберігання значення кожного вхідного поля форми.
- Написання обробника `onChange` для оновлення стану при кожному натисканні клавіші.
- Передавання змінної стану назад до властивості `value` вхідного поля.
Хоча це дає React повний контроль над станом форми, це вносить значну кількість шаблонів коду. Для форми з десятьма полями вам може знадобитися десять змінних стану та десять функцій обробників. Керування валідацією, станами помилок і статусом надсилання додає ще більше складності, що часто призводить до того, що розробники створюють складні власні хуки або звертаються до всеосяжних сторонніх бібліотек.
Такі бібліотеки, як Formik та React Hook Form, набули популярності, абстрагуючи цю складність. Вони надають чудові рішення для керування станом, валідації та оптимізації продуктивності. Однак вони представляють ще одну залежність, якою потрібно керувати, і часто працюють повністю на стороні клієнта, що може призвести до дублювання логіки валідації між фронтендом і бекендом.
Нова ера: Прогресивне покращення та Server Actions
React Server Actions представляють собою зміну парадигми. Основна ідея полягає в тому, щоб будувати на основі веб-платформи: стандартного HTML-елемента `
Простий приклад: Розумна кнопка надсилання
Давайте подивимося на найпоширеніший випадок використання в дії. Замість стандартного `
Файл: SubmitButton.js
import { experimental_useFormStatus as useFormStatus } from 'react-dom';
export function SubmitButton() {
const { pending } = useFormStatus();
return (
);
}
Файл: SignUpForm.js
import { SubmitButton } from './SubmitButton';
import { signUpAction } from './actions'; // A server action
export function SignUpForm() {
return (
У цьому прикладі `SubmitButton` є повністю самодостатньою. Він не отримує жодних пропсів. Він використовує `useFormStatus`, щоб знати, коли `SignUpForm` перебуває в стані очікування, і автоматично вимикає себе та змінює свій текст. Це потужний патерн для роз'єднання та створення багаторазових компонентів, які знають про форму.
Суть питання: Логіка валідації на основі статусу
Тепер ми підійшли до основної концепції. `useFormStatus` призначений не лише для станів завантаження; це ключовий фактор, що сприяє іншому способу мислення про валідацію.
Визначення "Валідація статусу"
Валідація на основі статусу - це патерн, де відгук про валідацію в основному надається користувачеві у відповідь на спробу надсилання форми. Замість валідації при кожному натисканні клавіші (`onChange`) або коли користувач залишає поле (`onBlur`), основна логіка валідації запускається, коли користувач надсилає форму. Результат цього надсилання - його *статус* (наприклад, успіх, помилка валідації, помилка сервера) - потім використовується для оновлення UI.
Цей підхід ідеально узгоджується з React Server Actions. Server action стає єдиним джерелом правди для валідації. Він отримує дані форми, перевіряє їх на відповідність вашим бізнес-правилам (наприклад, "чи вже використовується цей електронний лист?") і повертає структурований об'єкт стану, що вказує на результат.
Роль його партнера: `experimental_useFormState`
`useFormStatus` повідомляє нам, *що* відбувається (очікування), але він не повідомляє нам *результат* того, що сталося. Для цього нам потрібен його братський хук: `experimental_useFormState`.
`useFormState` - це хук, призначений для оновлення стану на основі результату дії форми. Він приймає функцію дії та початковий стан як аргументи та повертає новий стан і обгорнуту функцію дії для передавання у вашу форму.
const [state, formAction] = useFormState(myAction, initialState);
- `state`: Він міститиме значення, що повертається з останнього виконання `myAction`. Тут ми отримаємо наші повідомлення про помилки.
- `formAction`: Це нова версія вашої дії, яку ви повинні передати у властивість `action` `
`. Коли це буде викликано, це запустить оригінальну дію та оновиить `state`.
Об'єднаний робочий процес: Від кліка до відгуку
Ось як `useFormState` і `useFormStatus` працюють разом, щоб створити повний цикл валідації:
- Початковий рендеринг: Форма відображається з початковим станом, наданим `useFormState`. Помилки не відображаються.
- Надсилання користувачем: Користувач натискає кнопку надсилання.
- Стан очікування: Хук `useFormStatus` у кнопці надсилання негайно повідомляє `pending: true`. Кнопка стає неактивною та показує повідомлення про завантаження.
- Виконання дії: Server action (обгорнута `useFormState`) виконується з даними форми. Він виконує валідацію.
- Повернення дії: Дія не проходить валідацію та повертає об'єкт стану, наприклад:
`{ message: "Validation failed", errors: { email: "This email is already taken." } }` - Оновлення стану: `useFormState` отримує це значення, що повертається, і оновлює свою змінну `state`. Це запускає повторний рендеринг компонента форми.
- UI відгук: Форма повторно відтворюється. Статус `pending` з `useFormStatus` стає `false`. Тепер компонент може прочитати `state.errors.email` і відобразити повідомлення про помилку поруч із полем введення електронної пошти.
Увесь цей потік надає чіткий, авторитетний відгук користувачеві, повністю керований статусом надсилання та результатом.
Практичний майстер-клас: Створення багатокомпонентної форми реєстрації
Давайте закріпимо ці концепції, створивши повну форму реєстрації в стилі виробництва. Ми будемо використовувати server action для валідації та `useFormState` і `useFormStatus` для створення чудового користувацького досвіду.
Крок 1: Визначення Server Action з валідацією
Спочатку нам потрібен наш server action. Для надійної валідації ми будемо використовувати популярну бібліотеку Zod. Ця дія буде знаходитися в окремому файлі, позначеному директивою `'use server';`, якщо ви використовуєте такий фреймворк, як Next.js.
Файл: actions/authActions.js
'use server';
import { z } from 'zod';
// Define the validation schema
const registerSchema = z.object({
username: z.string().min(3, 'Username must be at least 3 characters long.'),
email: z.string().email('Please enter a valid email address.'),
password: z.string().min(8, 'Password must be at least 8 characters long.'),
});
// Define the initial state for our form
export const initialState = {
message: '',
errors: {},
};
export async function registerUser(prevState, formData) {
// 1. Validate the form data
const validatedFields = registerSchema.safeParse(
Object.fromEntries(formData.entries())
);
// 2. If validation fails, return the errors
if (!validatedFields.success) {
return {
message: 'Validation failed. Please check the fields.',
errors: validatedFields.error.flatten().fieldErrors,
};
}
// 3. (Simulate) Check if user already exists in the database
// In a real app, you would query your database here.
if (validatedFields.data.email === 'user@example.com') {
return {
message: 'Registration failed.',
errors: { email: ['This email is already registered.'] },
};
}
// 4. (Simulate) Create the user
console.log('Creating user:', validatedFields.data);
// 5. Return a success state
// In a real app, you might redirect here using `redirect()` from 'next/navigation'
return {
message: 'User registered successfully!',
errors: {},
};
}
Цей server action є мозком нашої форми. Він самодостатній, безпечний і надає чітку структуру даних як для успішних станів, так і для станів помилок.
Крок 2: Створення багаторазових компонентів, що знають про статус
Щоб зберегти наш основний компонент форми чистим, ми створимо спеціальні компоненти для наших вхідних даних і кнопки надсилання.
Файл: components/SubmitButton.js
'use client';
import { experimental_useFormStatus as useFormStatus } from 'react-dom';
export function SubmitButton({ label }) {
const { pending } = useFormStatus();
return (
);
}
Зверніть увагу на використання `aria-disabled={pending}`. Це важлива практика доступності, яка гарантує, що зчитувачі екрана правильно оголосять стан вимкнення.
Крок 3: Збірка основної форми за допомогою `useFormState`
Тепер давайте об'єднаємо все в нашому основному компоненті форми. Ми будемо використовувати `useFormState`, щоб підключити наш UI до дії `registerUser`.
Файл: components/RegistrationForm.js
{state.message} {state.message}
{state.errors.username[0]}
{state.errors.email[0]}
{state.errors.password[0]}
'use client';
import { experimental_useFormState as useFormState } from 'react-dom';
import { registerUser, initialState } from '../actions/authActions';
import { SubmitButton } from './SubmitButton';
export function RegistrationForm() {
const [state, formAction] = useFormState(registerUser, initialState);
return (
Зареєструватися
{state?.message && !state.errors &&
Цей компонент тепер декларативний і чистий. Він не керує жодним станом самостійно, окрім об'єкта `state`, наданого `useFormState`. Його єдина робота - відтворювати UI на основі цього стану. Логіка вимкнення кнопки інкапсульована в `SubmitButton`, а вся логіка валідації знаходиться в `authActions.js`. Це розділення проблем є величезною перемогою для зручності обслуговування.
Розширені методи та професійні найкращі практики
Хоча базовий шаблон є потужним, реальні програми часто вимагають більшої деталізації. Давайте розглянемо деякі розширені методи.
Гібридний підхід: Об'єднання миттєвої та пост-надсилання валідації
Валідація на основі статусу чудова для серверних перевірок, але очікування мережевого обходу, щоб повідомити користувачеві, що його електронний лист недійсний, може бути повільним. Гібридний підхід часто є найкращим:
- Використовуйте валідацію HTML5: Не забувайте про основи! Такі атрибути, як `required`, `type="email"`, `minLength` і `pattern`, надають миттєвий, вбудований у браузер зворотний зв'язок безкоштовно.
- Легка клієнтська валідація: Для чисто косметичних або форматуючих перевірок (наприклад, індикатор надійності пароля) ви все ще можете використовувати мінімальну кількість обробників `useState` і `onChange`.
- Авторитет на стороні сервера: Зарезервуйте server action для найважливішої валідації бізнес-логіки, яку неможливо виконати на клієнті (наприклад, перевірка унікальних імен користувачів, валідація за записами бази даних).
Це дає вам найкраще з обох світів: негайний відгук про прості помилки та авторитетна валідація для складних правил.
Доступність (A11y): Створення форм для всіх
Доступність не підлягає обговоренню. Під час реалізації валідації на основі статусу майте на увазі такі моменти:
- Оголошення помилок: У нашому прикладі ми використовували `aria-live="polite"` на контейнерах повідомлень про помилки. Це повідомляє зчитувачам екрана оголошувати повідомлення про помилку, як тільки воно з'явиться, не перериваючи поточний потік користувача.
- Пов'язуйте помилки з вхідними даними: Для більш надійного з'єднання використовуйте атрибут `aria-describedby`. Вхідні дані можуть вказувати на ідентифікатор контейнера повідомлень про помилки, створюючи програмний зв'язок.
- Керування фокусом: Після надсилання з помилками подумайте про програмне переміщення фокусу на перше недійсне поле. Це рятує користувачів від пошуку того, що пішло не так.
Оптимістичний UI з властивістю `data` `useFormStatus`
Уявіть собі програму соціальних мереж, де користувач публікує коментар. Замість того, щоб показувати спінер протягом секунди, ви можете зробити так, щоб програма відчувалася миттєвою. Властивість `data` з `useFormStatus` ідеально підходить для цього.
Коли форму буде надіслано, `pending` стає true, а `data` заповнюється `FormData` надсилання. Ви можете негайно відтворити новий коментар у тимчасовому, "очікуючому" візуальному стані, використовуючи ці `data`. Якщо server action буде успішним, ви заміните коментар, що очікує, остаточними даними з сервера. Якщо це не вдасться, ви можете видалити коментар, що очікує, і показати помилку. Це робить додаток неймовірно чуйним.
Навігація "Експериментальними" водами
Важливо звернути увагу на префікс "experimental" в `experimental_useFormStatus` і `experimental_useFormState`.
Що насправді означає "Експериментальний"
Коли React позначає API як експериментальний, це означає:
- API може змінитися: Ім'я, аргументи або значення, що повертаються, можуть бути змінені в майбутньому випуску React без дотримання стандартної семантичної версійності (SemVer) для змін, що порушують сумісність.
- Можуть бути помилки: Як нова функція, вона може мати крайні випадки, які ще не повністю зрозумілі або вирішені.
- Документація може бути розрідженою: Хоча основні концепції задокументовані, детальні посібники з розширених патернів можуть все ще розвиватися.
Коли приймати, а коли чекати
Отже, чи варто використовувати його у своєму проєкті? Відповідь залежить від вашого контексту:
- Добре для: Особисті проєкти, внутрішні інструменти, стартапи або команди, які комфортно керують потенційними змінами API. Використання його в рамках такого фреймворку, як Next.js (який інтегрував ці функції в свій App Router), зазвичай є більш безпечним варіантом, оскільки фреймворк може допомогти абстрагувати частину змін.
- Використовуйте з обережністю для: Великих корпоративних програм, критично важливих систем або проєктів з довгостроковими контрактами на обслуговування, де стабільність API має першорядне значення. У цих випадках може бути розумним почекати, поки хуки не будуть перенесені в стабільний API.
Завжди слідкуйте за офіційним блогом React і документацією, щоб дізнатися про оголошення щодо стабілізації цих хуків.
Висновок: Майбутнє форм у React
Впровадження `experimental_useFormStatus` і пов'язаних з ним API - це більше, ніж просто новий інструмент; це являє собою філософський зсув у тому, як ми створюємо інтерактивні враження з React. Приймаючи основи веб-платформи та спільно розміщуючи логіку зі станом на сервері, ми можемо створювати програми, які є простішими, більш стійкими та часто більш продуктивними.
Ми бачили, як `useFormStatus` надає чистий, відокремлений спосіб для компонентів реагувати на життєвий цикл надсилання форми. Він усуває прокидування пропсів для станів очікування та дозволяє створювати елегантні, самодостатні компоненти UI, такі як розумна `SubmitButton`. У поєднанні з `useFormState` він відкриває потужний патерн валідації на основі статусу, де сервер є остаточним авторитетом, а основним завданням клієнта є відтворення стану, повернутого server action.
Хоча тег "experimental" вимагає певного ступеня обережності, напрямок є зрозумілим. Майбутнє форм у React - це прогресивне покращення, спрощене керування станом і потужна, бездоганна інтеграція між клієнтською та серверною логікою. Освоюючи ці нові хуки сьогодні, ви не просто вивчаєте новий API; ви готуєтесь до наступного покоління веб-розробки з React.