Отключете мощна, модерна валидация на формуляри в React. Това цялостно ръководство разглежда експерименталния `useForm_Status` hook, сървърни действия и парадигмата на валидация на статус за изграждане на стабилни и производителни формуляри.
Овладяване на валидацията на формуляри с `experimental_useFormStatus` на React
Формулярите са крайъгълният камък на уеб взаимодействието. От просто записване за бюлетин до сложно многоетапно финансово приложение, те са основният канал, чрез който потребителите комуникират с нашите приложения. Въпреки това, години наред управлението на състоянието на формуляри в React беше източник на сложност, повтарящ се код и умора от зависимости. Жонглирахме с контролирани компоненти, борихме се с библиотеки за управление на състоянието и пишехме безброй `onChange` обработчици, всичко това в преследване на безпроблемно и интуитивно потребителско изживяване.
Екипът на React преосмисля този фундаментален аспект на уеб разработката, което води до въвеждането на нова, мощна парадигма, съсредоточена около React Server Actions. Този нов модел, изграден върху принципите на прогресивното подобряване, цели да опрости обработката на формуляри, като премести логиката по-близо до мястото, където й е мястото – често, сървъра. В основата на тази клиентска революция са два нови експериментални hook-а: `useFormState` и звездата на нашата дискусия днес, `experimental_useFormStatus`.
Това цялостно ръководство ще ви отведе на дълбоко гмуркане в `experimental_useFormStatus` hook-а. Няма просто да разгледаме неговия синтаксис; ще изследваме умствения модел, който той позволява: Логика за валидация, базирана на статус. Ще научите как този hook отделя потребителския интерфейс от състоянието на формуляра, опростява управлението на състояния на изчакване и работи в унисон със сървърни действия за създаване на стабилни, достъпни и високоефективни формуляри, които работят дори преди зареждането на JavaScript. Пригответе се да преосмислите всичко, което си мислехте, че знаете за изграждането на формуляри в React.
Парадигмална промяна: Еволюцията на React формулярите
За да оценим напълно иновацията, която `useFormStatus` носи, първо трябва да разберем пътешествието на управлението на формуляри в екосистемата на React. Този контекст подчертава проблемите, които този нов подход елегантно решава.
Старата гвардия: Контролирани компоненти и библиотеки на трети страни
Години наред стандартният подход към формулярите в React беше моделът на контролирани компоненти. Това включва:
- Използване на променлива за състояние в React (например от `useState`), за да се съхранява стойността на всеки вход във формуляра.
- Писане на `onChange` обработчик за актуализиране на състоянието при всяко натискане на клавиш.
- Предаване на променливата за състояние обратно към `value` prop-а на входа.
Докато това дава на React пълен контрол върху състоянието на формуляра, то въвежда значителен повтарящ се код. За формуляр с десет полета може да се нуждаете от десет променливи за състояние и десет функции за обработка. Управлението на валидация, състояния на грешки и статус на изпращане добавя още повече сложност, което често кара разработчиците да създават сложни потребителски hook-ове или да посягат към цялостни библиотеки на трети страни.
Библиотеки като Formik и React Hook Form набират популярност, като абстрахират тази сложност. Те предоставят брилянтни решения за управление на състоянието, валидация и оптимизация на производителността. Те обаче представляват още една зависимост за управление и често работят изцяло от страна на клиента, което може да доведе до дублиране на логиката за валидация между frontend и backend.
Новата ера: Прогресивно подобряване и сървърни действия
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` е напълно самостоятелен. Той не получава никакви props. Използва `useFormStatus`, за да знае кога `SignUpForm` е в процес на изчакване и автоматично се деактивира и променя текста си. Това е мощен модел за отделяне и създаване на преизползваеми, осведомени за формуляри компоненти.
Сърцевината на въпроса: Логика за валидация, базирана на статус
Сега стигаме до основната концепция. `useFormStatus` не е само за състояния на зареждане; той е ключов фактор за различен начин на мислене за валидация.
Дефиниране на „валидация на статус“
Валидация, базирана на статус, е модел, при който обратната връзка за валидация се предоставя предимно на потребителя в отговор на опит за изпращане на формуляр. Вместо да се валидира при всяко натискане на клавиш (`onChange`) или когато потребителят напусне поле (`onBlur`), основната логика за валидация се изпълнява, когато потребителят изпрати формуляра. Резултатът от това изпращане – неговият статус (например успех, грешка при валидация, грешка на сървъра) – след това се използва за актуализиране на потребителския интерфейс.
Този подход идеално съответства на React Server Actions. Сървърното действие става единственият източник на истина за валидация. То получава данните от формуляра, валидира ги спрямо вашите бизнес правила (например „имейлът вече зает ли е?“) и връща структуриран обект за състояние, указващ крайния резултат.
Ролята на неговия партньор: `experimental_useFormState`
`useFormStatus` ни казва какво се случва (в процес на изчакване), но не ни казва резултата от случилото се. За това се нуждаем от неговия сестрински hook: `experimental_useFormState`.
`useFormState` е hook, предназначен да актуализира състоянието въз основа на резултата от действие на формуляр. Той приема функцията за действие и първоначално състояние като аргументи и връща ново състояние и обвита функция за действие, която да предадете на вашия формуляр.
const [state, formAction] = useFormState(myAction, initialState);
- `state`: Това ще съдържа върнатата стойност от последното изпълнение на `myAction`. Тук ще получим съобщенията за грешки.
- `formAction`: Това е нова версия на вашето действие, която трябва да предадете на `action` prop-а на `
`. Когато бъде извикана, тя ще задейства оригиналното действие и ще актуализира `state`.
Комбинираният работен процес: От кликване до обратна връзка
Ето как `useFormState` и `useFormStatus` работят заедно, за да създадат пълен цикъл на валидация:
- Първоначално рендиране: Формулярът се рендира с първоначално състояние, предоставено от `useFormState`. Грешки не се показват.
- Потребителско изпращане: Потребителят кликва върху бутона за изпращане.
- Състояние на изчакване: `useFormStatus` hook-ът в бутона за изпращане незабавно докладва `pending: true`. Бутонът се деактивира и показва съобщение за зареждане.
- Изпълнение на действие: Сървърното действие (обвито от `useFormState`) се изпълнява с данните от формуляра. То извършва валидация.
- Действието връща: Действието не успява да валидира и връща обект със състояние, например:
`{ message: "Validation failed", errors: { email: "This email is already taken." } }` - Актуализация на състоянието: `useFormState` получава тази върната стойност и актуализира своята `state` променлива. Това задейства повторно рендиране на компонента на формуляра.
- Обратна връзка в UI: Формулярът се рендира отново. `pending` статусът от `useFormStatus` става `false`. Компонентът вече може да прочете `state.errors.email` и да покаже съобщението за грешка до полето за имейл.
Този цялостен процес предоставя ясна, сървърно-авторитетна обратна връзка на потребителя, задвижвана изцяло от статуса и резултата на изпращането.
Практическа майсторска класа: Създаване на многоетапна регистрационна форма
Нека затвърдим тези концепции, като изградим пълна, професионална регистрационна форма. Ще използваме сървърно действие за валидация и двата hook-а `useFormState` и `useFormStatus`, за да създадем страхотно потребителско изживяване.
Стъпка 1: Дефиниране на сървърното действие с валидация
Първо, се нуждаем от нашето сървърно действие. За стабилна валидация ще използваме популярната библиотека 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: {},
};
}
Това сървърно действие е мозъкът на нашия формуляр. То е самостоятелно, сигурно и предоставя ясна структура на данните както за успех, така и за грешки.
Стъпка 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`, за да свържем нашия потребителски интерфейс със `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 (
Register
{state?.message && !state.errors &&
Този компонент вече е декларативен и чист. Той не управлява никакво състояние сам, освен `state` обекта, предоставен от `useFormState`. Неговата единствена задача е да рендира потребителския интерфейс въз основа на това състояние. Логиката за деактивиране на бутона е капсулирана в `SubmitButton`, а цялата логика за валидация се намира в `authActions.js`. Това разделение на отговорностите е огромна победа за поддръжката.
Разширени техники и професионални най-добри практики
Докато основният модел е мощен, реалните приложения често изискват повече нюанси. Нека разгледаме някои разширени техники.
Хибриден подход: Смесване на незабавна и валидация след изпращане
Валидацията, базирана на статус, е отлична за сървърни проверки, но чакането на мрежово пътуване, за да се каже на потребителя, че имейлът му е невалиден, може да бъде бавно. Хибридният подход често е най-добър:
- Използвайте HTML5 валидация: Не забравяйте основите! Атрибути като `required`, `type="email"`, `minLength` и `pattern` предоставят незабавна, нативна обратна връзка от браузъра без никакви разходи.
- Лека клиентска валидация: За чисто козметични проверки или проверки на форматиране (например индикатор за сила на паролата), все още можете да използвате минимално количество `useState` и `onChange` обработчици.
- Сървърна авторитетност: Запазете сървърното действие за най-критичната валидация на бизнес логика, която не може да бъде извършена на клиента (например проверка за уникални потребителски имена, валидиране спрямо записи в база данни).
Това ви дава най-доброто от двата свята: незабавна обратна връзка за прости грешки и авторитетна валидация за сложни правила.
Достъпност (A11y): Изграждане на формуляри за всички
Достъпността е задължителна. При имплементиране на валидация, базирана на статус, имайте предвид тези точки:
- Обявяване на грешки: В нашия пример използвахме `aria-live="polite"` на контейнерите за съобщения за грешки. Това казва на екранните четци да обявят съобщението за грешка веднага щом се появи, без да прекъсват текущия поток на потребителя.
- Свързване на грешки с входове: За по-стабилна връзка използвайте `aria-describedby` атрибута. Входът може да посочва ID-то на контейнера за съобщение за грешка, създавайки програмна връзка.
- Управление на фокуса: След изпращане с грешки, обмислете програмно преместване на фокуса към първото невалидно поле. Това спестява на потребителите търсенето какво се е объркало.
Оптимистичен UI с `data` свойството на `useFormStatus`
Представете си социална медийна платформа, където потребител публикува коментар. Вместо да показвате индикатор за зареждане за секунда, можете да направите приложението да се чувства мигновено. `data` свойството от `useFormStatus` е идеално за това.
Когато формулярът бъде изпратен, `pending` става true и `data` се запълва с `FormData` на изпращането. Можете незабавно да рендирате новия коментар в временно, „очакващо“ визуално състояние, използвайки тези `data`. Ако сървърното действие успее, заменяте временно коментара с финалните данни от сървъра. Ако се провали, можете да премахнете временно коментара и да покажете грешка. Това прави приложението да се чувства изключително отзивчиво.
Навигация в „Експерименталните“ води
Жизненоважно е да се обърнем към префикса „експериментален“ в `experimental_useFormStatus` и свързаните с него API-та.
Какво означава „Експериментален“ всъщност
Когато React етикетира API като експериментален, това означава:
- API може да се промени: Името, аргументите или върнатите стойности могат да бъдат променени в бъдещо издание на React, без да се следва стандартното семантично версиониране (SemVer) за промени, нарушаващи обратно съвместимост.
- Може да има грешки: Като нова функция, тя може да има гранични случаи, които все още не са напълно разбрани или разрешени.
- Документацията може да е оскъдна: Докато основните концепции са документирани, подробни ръководства за разширени модели може все още да се развиват.
Кога да се приеме и кога да се изчака
И така, трябва ли да го използвате във вашия проект? Отговорът зависи от вашия контекст:
- Добро за: Лични проекти, вътрешни инструменти, стартъпи или екипи, които се чувстват комфортно с управлението на потенциални промени в API. Използването му в рамките на рамка като Next.js (която е интегрирала тези функции в своя App Router) като цяло е по-безопасен залог, тъй като рамката може да помогне за абстрахиране на част от промените.
- Използвайте с повишено внимание за: Мащабни корпоративни приложения, критични системи или проекти с дългосрочни договори за поддръжка, където стабилността на API е от първостепенно значение. В тези случаи може да е разумно да изчакате, докато hook-овете бъдат повишени до стабилно API.
Винаги следете официалния блог и документацията на React за съобщения относно стабилизирането на тези hook-ове.
Заключение: Бъдещето на формулярите в React
Въвеждането на `experimental_useFormStatus` и свързаните с него API-та е повече от просто нов инструмент; то представлява философска промяна в начина, по който изграждаме интерактивни преживявания с React. Приемайки основите на уеб платформата и съвместното разполагане на състоянието на логиката на сървъра, можем да изграждаме приложения, които са по-прости, по-устойчиви и често по-производителни.
Видяхме как `useFormStatus` предоставя чист, отделен начин компонентите да реагират на жизнения цикъл на изпращането на формуляр. Той елиминира предаването на props за състояния на изчакване и позволява елегантни, самостоятелни UI компоненти като интелигентен `SubmitButton`. Когато се комбинира с `useFormState`, той отключва мощния модел на валидация, базирана на статус, при който сървърът е крайната авторитетна инстанция, а основната отговорност на клиента е да рендира състоянието, върнато от сървърното действие.
Докато „експерименталният“ етикет оправдава известна предпазливост, посоката е ясна. Бъдещето на формулярите в React е едно на прогресивно подобряване, опростено управление на състоянието и мощна, безпроблемна интеграция между клиентската и сървърната логика. Като овладявате тези нови hook-ове днес, вие не просто научавате нов API; вие се подготвяте за следващото поколение разработка на уеб приложения с React.