اعتبارسنجی فرم قدرتمند و مدرن را در React فعال کنید. این راهنمای جامع به بررسی هوک experimental_useFormStatus، اکشنهای سرور و پارادایم اعتبارسنجی مبتنی بر وضعیت برای ساخت فرمهای قوی و کارآمد میپردازد.
تسلط بر اعتبارسنجی فرم با هوک `experimental_useFormStatus` در React
فرمها سنگ بنای تعامل در وب هستند. از یک ثبتنام ساده در خبرنامه گرفته تا یک اپلیکیشن مالی چند مرحلهای پیچیده، آنها کانال اصلی ارتباط کاربران با اپلیکیشنهای ما هستند. با این حال، سالهاست که مدیریت وضعیت فرم در React منبعی از پیچیدگی، کدهای تکراری (boilerplate) و خستگی ناشی از وابستگیها بوده است. ما با کامپوننتهای کنترلشده دست و پنجه نرم کردهایم، با کتابخانههای مدیریت وضعیت جنگیدهایم و بیشمار handler برای `onChange` نوشتهایم، همه اینها در راستای دستیابی به یک تجربه کاربری روان و شهودی بوده است.
تیم React در حال بازنگری در این جنبه بنیادین توسعه وب بوده است که منجر به معرفی یک پارادایم جدید و قدرتمند با محوریت اکشنهای سرور ریاکت (React Server Actions) شده است. این مدل جدید که بر پایه اصول بهبود تدریجی (progressive enhancement) ساخته شده، با انتقال منطق به جایی که به آن تعلق دارد - یعنی اغلب سرور - به دنبال سادهسازی مدیریت فرم است. در قلب این انقلاب سمت کلاینت، دو هوک آزمایشی جدید قرار دارند: `useFormState` و ستاره بحث امروز ما، `experimental_useFormStatus`.
این راهنمای جامع شما را به یک شیرجه عمیق در هوک `experimental_useFormStatus` میبرد. ما فقط به سینتکس آن نگاه نخواهیم کرد؛ بلکه مدل ذهنی را که این هوک امکانپذیر میکند، بررسی خواهیم کرد: منطق اعتبارسنجی مبتنی بر وضعیت (Status-Based Validation Logic). شما یاد خواهید گرفت که چگونه این هوک رابط کاربری (UI) را از وضعیت فرم جدا میکند، مدیریت حالتهای در حال انتظار (pending) را ساده میسازد و در هماهنگی با اکشنهای سرور برای ایجاد فرمهای قوی، دسترسپذیر و با کارایی بالا که حتی قبل از بارگذاری جاوااسکریپت کار میکنند، عمل میکند. آماده شوید تا همه آنچه را که در مورد ساخت فرم در React میدانستید، از نو بیاموزید.
یک تغییر پارادایم: تکامل فرمها در React
برای درک کامل نوآوری که `useFormStatus` به ارمغان میآورد، ابتدا باید سفر مدیریت فرم در اکوسیستم React را درک کنیم. این زمینه مشکلاتی را که این رویکرد جدید به زیبایی حل میکند، برجسته میسازد.
گارد قدیمی: کامپوننتهای کنترلشده و کتابخانههای شخص ثالث
برای سالها، رویکرد استاندارد برای فرمها در React الگوی کامپوننت کنترلشده (controlled component) بود. این الگو شامل موارد زیر است:
- استفاده از یک متغیر وضعیت React (مثلاً از `useState`) برای نگهداری مقدار هر ورودی فرم.
- نوشتن یک handler برای `onChange` جهت بهروزرسانی وضعیت با هر بار فشردن کلید.
- ارسال متغیر وضعیت به پراپ `value` ورودی.
در حالی که این روش به React کنترل کامل بر وضعیت فرم را میدهد، اما کد تکراری (boilerplate) قابل توجهی را به همراه دارد. برای یک فرم با ده فیلد، ممکن است به ده متغیر وضعیت و ده تابع handler نیاز داشته باشید. مدیریت اعتبارسنجی، وضعیتهای خطا و وضعیت ارسال فرم، پیچیدگی بیشتری را اضافه میکند و اغلب توسعهدهندگان را به سمت ایجاد هوکهای سفارشی پیچیده یا استفاده از کتابخانههای جامع شخص ثالث سوق میدهد.
کتابخانههایی مانند Formik و React Hook Form با انتزاعی کردن این پیچیدگی به شهرت رسیدند. آنها راهحلهای درخشانی برای مدیریت وضعیت، اعتبارسنجی و بهینهسازی عملکرد ارائه میدهند. با این حال، آنها یک وابستگی دیگر برای مدیریت هستند و اغلب به طور کامل در سمت کلاینت عمل میکنند، که میتواند منجر به تکرار منطق اعتبارسنجی بین فرانتاند و بکاند شود.
دوران جدید: بهبود تدریجی و اکشنهای سرور
اکشنهای سرور ریاکت یک تغییر پارادایم را معرفی میکنند. ایده اصلی، ساختن بر پایه پلتفرم وب است: عنصر استاندارد 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'; // یک اکشن سرور
export function SignUpForm() {
return (
در این مثال، `SubmitButton` کاملاً مستقل است. هیچ پراپی دریافت نمیکند. از `useFormStatus` استفاده میکند تا بفهمد `SignUpForm` چه زمانی در حالت pending است و به طور خودکار خود را غیرفعال کرده و متن خود را تغییر میدهد. این یک الگوی قدرتمند برای جداسازی و ایجاد کامپوننتهای قابل استفاده مجدد و آگاه از فرم است.
قلب موضوع: منطق اعتبارسنجی مبتنی بر وضعیت
اکنون به مفهوم اصلی میرسیم. `useFormStatus` فقط برای حالتهای بارگذاری نیست؛ بلکه یک عامل کلیدی برای روشی متفاوت از تفکر در مورد اعتبارسنجی است.
تعریف «اعتبارسنجی وضعیت»
اعتبارسنجی مبتنی بر وضعیت (Status-Based Validation) الگویی است که در آن بازخورد اعتبارسنجی عمدتاً در پاسخ به تلاش برای ارسال فرم به کاربر ارائه میشود. به جای اعتبارسنجی با هر بار فشردن کلید (`onChange`) یا زمانی که کاربر یک فیلد را ترک میکند (`onBlur`)، منطق اصلی اعتبارسنجی هنگام ارسال فرم توسط کاربر اجرا میشود. نتیجه این ارسال - یعنی *وضعیت* آن (مثلاً موفقیت، خطای اعتبارسنجی، خطای سرور) - سپس برای بهروزرسانی UI استفاده میشود.
این رویکرد کاملاً با اکشنهای سرور ریاکت هماهنگ است. اکشن سرور به تنها منبع حقیقت برای اعتبارسنجی تبدیل میشود. دادههای فرم را دریافت میکند، آن را در برابر قوانین کسبوکار شما اعتبارسنجی میکند (مثلاً «آیا این ایمیل قبلاً استفاده شده است؟»)، و یک آبجکت وضعیت ساختاریافته را که نشاندهنده نتیجه است، برمیگرداند.
نقش شریک آن: `experimental_useFormState`
`useFormStatus` به ما میگوید *چه اتفاقی* در حال رخ دادن است (pending)، اما نتیجه اتفاقی که افتاده را به ما نمیگوید. برای این کار، به هوک خواهر آن نیاز داریم: `experimental_useFormState`.
`useFormState` یک هوک است که برای بهروزرسانی وضعیت بر اساس نتیجه یک اکشن فرم طراحی شده است. این هوک تابع اکشن و یک وضعیت اولیه را به عنوان آرگومان میگیرد و یک وضعیت جدید و یک تابع اکشن پیچیدهشده را برای ارسال به فرم شما برمیگرداند.
const [state, formAction] = useFormState(myAction, initialState);
- `state`: این شامل مقدار بازگشتی از آخرین اجرای `myAction` خواهد بود. اینجاست که ما پیامهای خطای خود را دریافت خواهیم کرد.
- `formAction`: این یک نسخه جدید از اکشن شما است که باید به پراپ `action` عنصر `
` ارسال کنید. هنگامی که این فراخوانی میشود، اکشن اصلی را فعال کرده و `state` را بهروز میکند.
جریان کاری ترکیبی: از کلیک تا بازخورد
در اینجا نحوه همکاری `useFormState` و `useFormStatus` برای ایجاد یک حلقه اعتبارسنجی کامل آمده است:
- رندر اولیه: فرم با یک وضعیت اولیه که توسط `useFormState` ارائه شده، رندر میشود. هیچ خطایی نمایش داده نمیشود.
- ارسال کاربر: کاربر روی دکمه ارسال کلیک میکند.
- حالت Pending: هوک `useFormStatus` در دکمه ارسال بلافاصله `pending: true` را گزارش میدهد. دکمه غیرفعال شده و یک پیام بارگذاری نشان میدهد.
- اجرای اکشن: اکشن سرور (که توسط `useFormState` پیچیده شده) با دادههای فرم اجرا میشود و اعتبارسنجی را انجام میدهد.
- بازگشت اکشن: اکشن در اعتبارسنجی ناموفق است و یک آبجکت وضعیت برمیگرداند، به عنوان مثال:
`{ message: "Validation failed", errors: { email: "This email is already taken." } }` - بهروزرسانی وضعیت: `useFormState` این مقدار بازگشتی را دریافت کرده و متغیر `state` خود را بهروز میکند. این باعث رندر مجدد کامپوننت فرم میشود.
- بازخورد UI: فرم دوباره رندر میشود. وضعیت `pending` از `useFormStatus` به `false` تبدیل میشود. کامپوننت اکنون میتواند `state.errors.email` را بخواند و پیام خطا را در کنار فیلد ورودی ایمیل نمایش دهد.
این کل جریان، بازخورد واضح و معتبر از سمت سرور را برای کاربر فراهم میکند که کاملاً توسط وضعیت و نتیجه ارسال هدایت میشود.
مسترکلاس عملی: ساخت یک فرم ثبتنام چند فیلدی
بیایید این مفاهیم را با ساخت یک فرم ثبتنام کامل و به سبک تولید، تثبیت کنیم. ما از یک اکشن سرور برای اعتبارسنجی و از هر دو هوک `useFormState` و `useFormStatus` برای ایجاد یک تجربه کاربری عالی استفاده خواهیم کرد.
مرحله ۱: تعریف اکشن سرور با اعتبارسنجی
ابتدا، ما به اکشن سرور خود نیاز داریم. برای اعتبارسنجی قوی، از کتابخانه محبوب Zod استفاده خواهیم کرد. این اکشن در یک فایل جداگانه قرار خواهد گرفت و اگر از فریمورکی مانند Next.js استفاده میکنید، با دستورالعمل `'use server';` مشخص میشود.
فایل: actions/authActions.js
'use server';
import { z } from 'zod';
// تعریف اسکیمای اعتبارسنجی
const registerSchema = z.object({
username: z.string().min(3, 'نام کاربری باید حداقل ۳ کاراکتر باشد.'),
email: z.string().email('لطفاً یک آدرس ایمیل معتبر وارد کنید.'),
password: z.string().min(8, 'رمز عبور باید حداقل ۸ کاراکتر باشد.'),
});
// تعریف وضعیت اولیه برای فرم ما
export const initialState = {
message: '',
errors: {},
};
export async function registerUser(prevState, formData) {
// ۱. اعتبارسنجی دادههای فرم
const validatedFields = registerSchema.safeParse(
Object.fromEntries(formData.entries())
);
// ۲. اگر اعتبارسنجی ناموفق بود، خطاها را برگردانید
if (!validatedFields.success) {
return {
message: 'اعتبارسنجی ناموفق بود. لطفاً فیلدها را بررسی کنید.',
errors: validatedFields.error.flatten().fieldErrors,
};
}
// ۳. (شبیهسازی) بررسی کنید که آیا کاربر قبلاً در پایگاه داده وجود دارد یا خیر
// در یک برنامه واقعی، شما در اینجا از پایگاه داده خود پرسوجو میکنید.
if (validatedFields.data.email === 'user@example.com') {
return {
message: 'ثبت نام ناموفق بود.',
errors: { email: ['این ایمیل قبلاً ثبت شده است.'] },
};
}
// ۴. (شبیهسازی) ایجاد کاربر
console.log('ایجاد کاربر:', validatedFields.data);
// ۵. بازگرداندن وضعیت موفقیت
// در یک برنامه واقعی، ممکن است در اینجا با استفاده از `redirect()` از 'next/navigation' کاربر را هدایت کنید
return {
message: 'کاربر با موفقیت ثبت نام کرد!',
errors: {},
};
}
این اکشن سرور، مغز فرم ما است. این اکشن مستقل، امن و ساختار داده واضحی برای هر دو حالت موفقیت و خطا فراهم میکند.
مرحله ۲: ساخت کامپوننتهای قابل استفاده مجدد و آگاه از وضعیت
برای تمیز نگه داشتن کامپوننت اصلی فرم، کامپوننتهای اختصاصی برای ورودیها و دکمه ارسال خود ایجاد خواهیم کرد.
فایل: components/SubmitButton.js
'use client';
import { experimental_useFormStatus as useFormStatus } from 'react-dom';
export function SubmitButton({ label }) {
const { pending } = useFormStatus();
return (
);
}
به استفاده از `aria-disabled={pending}` توجه کنید. این یک عمل مهم برای دسترسپذیری است که اطمینان میدهد صفحهخوانها حالت غیرفعال را به درستی اعلام میکنند.
مرحله ۳: مونتاژ فرم اصلی با `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` و handlerهای `onChange` استفاده کنید.
- اقتدار سمت سرور: اکشن سرور را برای مهمترین اعتبارسنجیهای منطق کسبوکار که نمیتوان در سمت کلاینت انجام داد (مثلاً بررسی نامهای کاربری منحصر به فرد، اعتبارسنجی در برابر رکوردهای پایگاه داده) رزرو کنید.
این به شما بهترینهای هر دو دنیا را میدهد: بازخورد فوری برای خطاهای ساده و اعتبارسنجی معتبر برای قوانین پیچیده.
دسترسپذیری (A11y): ساخت فرم برای همه
دسترسپذیری غیرقابل مذاکره است. هنگام پیادهسازی اعتبارسنجی مبتنی بر وضعیت، این نکات را در ذهن داشته باشید:
- اعلام خطاها: در مثال ما، از `aria-live="polite"` در کانتینرهای پیام خطا استفاده کردیم. این به صفحهخوانها میگوید که پیام خطا را به محض ظاهر شدن، بدون قطع جریان فعلی کاربر، اعلام کنند.
- مرتبط کردن خطاها با ورودیها: برای ارتباط قویتر، از ویژگی `aria-describedby` استفاده کنید. ورودی میتواند به ID کانتینر پیام خطای خود اشاره کند و یک پیوند برنامهریزی شده ایجاد کند.
- مدیریت فوکوس: پس از ارسال با خطا، در نظر بگیرید که فوکوس را به صورت برنامهریزی شده به اولین فیلد نامعتبر منتقل کنید. این کار کاربران را از جستجوی آنچه اشتباه بوده است، بینیاز میکند.
UI خوشبینانه با ویژگی `data` در `useFormStatus`
یک اپلیکیشن رسانه اجتماعی را تصور کنید که در آن کاربر یک نظر ارسال میکند. به جای نشان دادن یک اسپینر برای یک ثانیه، میتوانید کاری کنید که برنامه فوری به نظر برسد. ویژگی `data` از `useFormStatus` برای این کار عالی است.
هنگامی که فرم ارسال میشود، `pending` به `true` تبدیل شده و `data` با `FormData` ارسالی پر میشود. شما میتوانید بلافاصله نظر جدید را در یک حالت بصری موقت و 'در حال انتظار' با استفاده از این `data` رندر کنید. اگر اکشن سرور موفقیتآمیز بود، نظر در حال انتظار را با دادههای نهایی از سرور جایگزین میکنید. اگر ناموفق بود، میتوانید نظر در حال انتظار را حذف کرده و یک خطا نشان دهید. این باعث میشود برنامه به طرز باورنکردنی پاسخگو به نظر برسد.
پیمایش در آبهای «آزمایشی»
پرداختن به پیشوند «experimental» در `experimental_useFormStatus` و `experimental_useFormState` بسیار مهم است.
«آزمایشی» واقعاً به چه معناست
وقتی React یک API را به عنوان آزمایشی برچسبگذاری میکند، به این معنی است:
- API ممکن است تغییر کند: نام، آرگومانها یا مقادیر بازگشتی ممکن است در یک نسخه آینده React بدون پیروی از نسخهبندی معنایی استاندارد (SemVer) برای تغییرات شکننده، تغییر کنند.
- ممکن است باگ وجود داشته باشد: به عنوان یک ویژگی جدید، ممکن است موارد مرزی داشته باشد که هنوز به طور کامل درک یا حل نشدهاند.
- مستندات ممکن است پراکنده باشد: در حالی که مفاهیم اصلی مستند شدهاند، راهنماهای دقیق در مورد الگوهای پیشرفته ممکن است هنوز در حال تکامل باشند.
چه زمانی استفاده کنیم و چه زمانی صبر کنیم
پس، آیا باید از آن در پروژه خود استفاده کنید؟ پاسخ به زمینه شما بستگی دارد:
- مناسب برای: پروژههای شخصی، ابزارهای داخلی، استارتاپها، یا تیمهایی که با مدیریت تغییرات احتمالی API راحت هستند. استفاده از آن در چارچوبی مانند Next.js (که این ویژگیها را در App Router خود ادغام کرده) به طور کلی یک انتخاب امنتر است، زیرا چارچوب میتواند به انتزاعی کردن برخی از این تغییرات کمک کند.
- با احتیاط استفاده کنید برای: برنامههای کاربردی سازمانی در مقیاس بزرگ، سیستمهای حیاتی، یا پروژههایی با قراردادهای نگهداری بلندمدت که در آنها ثبات API از اهمیت بالایی برخوردار است. در این موارد، ممکن است عاقلانه باشد که تا زمانی که هوکها به یک API پایدار ارتقا یابند، صبر کنید.
همیشه وبلاگ رسمی و مستندات React را برای اطلاعیههای مربوط به پایدار شدن این هوکها زیر نظر داشته باشید.
نتیجهگیری: آینده فرمها در React
معرفی `experimental_useFormStatus` و APIهای مرتبط با آن چیزی بیش از یک ابزار جدید است؛ این نشاندهنده یک تغییر فلسفی در نحوه ساخت تجربیات تعاملی با React است. با پذیرش بنیانهای پلتفرم وب و قرار دادن منطق حالتدار در سرور، میتوانیم برنامههایی بسازیم که سادهتر، مقاومتر و اغلب کارآمدتر هستند.
ما دیدیم که چگونه `useFormStatus` یک روش تمیز و جدا شده برای واکنش کامپوننتها به چرخه حیات ارسال فرم فراهم میکند. این هوک نیاز به ارسال پراپ برای حالتهای pending را از بین میبرد و کامپوننتهای UI زیبا و مستقلی مانند یک `SubmitButton` هوشمند را امکانپذیر میسازد. هنگامی که با `useFormState` ترکیب میشود، الگوی قدرتمند اعتبارسنجی مبتنی بر وضعیت را باز میکند، جایی که سرور مرجع نهایی است و مسئولیت اصلی کلاینت، رندر کردن وضعیتی است که توسط اکشن سرور بازگردانده میشود.
در حالی که برچسب «آزمایشی» نیاز به درجهای از احتیاط دارد، مسیر روشن است. آینده فرمها در React، آیندهای از بهبود تدریجی، مدیریت وضعیت سادهشده و یکپارچگی قدرتمند و یکپارچه بین منطق کلاینت و سرور است. با تسلط بر این هوکهای جدید امروز، شما فقط یک API جدید را یاد نمیگیرید؛ شما در حال آماده شدن برای نسل بعدی توسعه برنامههای وب با React هستید.