افتح التحقق القوي والتدريجي في نماذج React متعددة المراحل. تعلم كيفية الاستفادة من خطاف useFormState لتجربة مستخدم سلسة ومتكاملة مع الخادم.
محرك التحقق من الصحة React useFormState: تعمق في التحقق من صحة النماذج متعددة المراحل
في عالم تطوير الويب الحديث، يعد إنشاء تجارب مستخدم بديهية وقوية أمرًا بالغ الأهمية. ولا يوجد مكان يكون فيه هذا الأمر أكثر أهمية من النماذج، وهي البوابة الأساسية لتفاعل المستخدم. بينما تكون نماذج الاتصال البسيطة واضحة، فإن التعقيد يتزايد بشكل كبير مع النماذج متعددة المراحل—فكر في معالجات تسجيل المستخدمين، أو عمليات الدفع للتجارة الإلكترونية، أو لوحات التكوين التفصيلية. تقدم هذه العمليات متعددة الخطوات تحديات كبيرة في إدارة الحالة، والتحقق من الصحة، والحفاظ على تدفق مستخدم سلس. تاريخياً، كان المطورون يتلاعبون بحالة معقدة من جانب العميل، ومقدمي السياق، ومكتبات الطرف الثالث لترويض هذا التعقيد.
أدخل خطاف `useFormState` من React. تم تقديمه كجزء من تطور React نحو مكونات متكاملة مع الخادم، يقدم هذا الخطاف القوي حلاً مبسطًا وأنيقًا لإدارة حالة النموذج والتحقق من صحتها، خاصة في سياق النماذج متعددة المراحل. من خلال التكامل المباشر مع إجراءات الخادم (Server Actions)، ينشئ `useFormState` محرك تحقق قوي يبسط الكود، ويعزز الأداء، ويدعم التحسين التدريجي. يقدم هذا المقال دليلاً شاملاً للمطورين في جميع أنحاء العالم حول كيفية بناء محرك تحقق متطور متعدد المراحل باستخدام `useFormState`، محولاً مهمة معقدة إلى عملية قابلة للإدارة والتوسع.
التحدي المستمر للنماذج متعددة المراحل
قبل الغوص في الحل، من الضروري فهم نقاط الألم الشائعة التي يواجهها المطورون مع النماذج متعددة المراحل. هذه التحديات ليست تافهة ويمكن أن تؤثر على كل شيء من وقت التطوير إلى تجربة المستخدم النهائي.
- تعقيد إدارة الحالة: كيف تحتفظ بالبيانات أثناء تنقل المستخدم بين الخطوات؟ هل يجب أن تعيش الحالة في مكون رئيسي، أو سياق عام، أو تخزين محلي؟ لكل نهج مقايضاته، وغالبًا ما يؤدي إلى تمرير الخصائص (prop-drilling) أو منطق مزامنة حالة معقدة.
- تجزئة منطق التحقق: أين يجب أن يحدث التحقق؟ التحقق من كل شيء في النهاية يوفر تجربة مستخدم سيئة. التحقق في كل خطوة أفضل، ولكن هذا يتطلب غالبًا كتابة منطق تحقق مجزأ، سواء على العميل (للحصول على رد فعل فوري) أو على الخادم (للأمان وسلامة البيانات).
- عقبات تجربة المستخدم: يتوقع المستخدم أن يكون قادرًا على التنقل ذهابًا وإيابًا بين الخطوات دون فقدان بياناته. ويتوقع أيضًا رسائل خطأ واضحة وسياقية ورد فعل فوري. يمكن أن يتضمن تطبيق هذه التجربة المرنة الكثير من التعليمات البرمجية المتكررة (boilerplate code).
- مزامنة حالة الخادم والعميل: المصدر النهائي للحقيقة هو عادة الخادم. الحفاظ على مزامنة حالة جانب العميل بشكل مثالي مع قواعد التحقق من صحة جانب الخادم ومنطق العمل هو معركة مستمرة، وغالبًا ما يؤدي إلى تكرار التعليمات البرمجية وتناقضات محتملة.
تُبرز هذه التحديات الحاجة إلى نهج أكثر تكاملاً وتماسكًا - نهج يسد الفجوة بين العميل والخادم. وهذا هو بالضبط حيث يتألق `useFormState`.
أدخل `useFormState`: نهج حديث لمعالجة النماذج
تم تصميم خطاف `useFormState` لإدارة حالة النموذج التي يتم تحديثها بناءً على نتيجة إجراء النموذج. إنه حجر الزاوية في رؤية React للتطبيقات المحسنة تدريجياً التي تعمل بسلاسة مع أو بدون تمكين JavaScript على العميل.
ما هو `useFormState`؟
في جوهره، `useFormState` هو خطاف React يأخذ حجتين: دالة إجراء خادم وحالة أولية. يعيد مصفوفة تحتوي على قيمتين: الحالة الحالية للنموذج ودالة إجراء جديدة لتمريرها إلى عنصر `
);
}
الخطوة 1: التقاط المعلومات الشخصية والتحقق منها
في هذه الخطوة، نريد فقط التحقق من حقلي `name` و `email`. سنستخدم مدخلًا مخفيًا `_step` لإخبار إجراء الخادم الخاص بنا بمنطق التحقق الذي يجب تشغيله.
// Step1.jsx component
{state.errors.name} {state.errors.email}
export function Step1({ state }) {
return (
الخطوة 1: المعلومات الشخصية
{state.errors?.name &&
{state.errors?.email &&
);
}
الآن، لنقم بتحديث إجراء الخادم الخاص بنا للتعامل مع التحقق من صحة الخطوة 1.
// actions.js (updated)
// ... (الواردات وتعريف المخطط)
export async function onbordingAction(prevState, formData) {
// ... (الحصول على بيانات النموذج)
const step = Number(formData.get('_step'));
if (step === 1) {
const validatedFields = schema.pick({ name: true, email: true }).safeParse({ name, email });
if (!validatedFields.success) {
return {
...currentState,
step: 1,
errors: validatedFields.error.flatten().fieldErrors,
};
}
// نجاح، انتقل إلى الخطوة التالية
return {
...currentState,
step: 2,
errors: {},
};
}
// ... (منطق للخطوات الأخرى)
}
عندما ينقر المستخدم على "التالي"، يتم إرسال النموذج. يتحقق إجراء الخادم من أنها الخطوة 1، ويتحقق من صحة حقلي فقط `name` و `email` باستخدام طريقة `pick` من Zod، ويعيد حالة جديدة. إذا فشل التحقق، فإنه يعيد الأخطاء ويبقى في الخطوة 1. إذا نجح، فإنه يمسح الأخطاء ويحدث `step` إلى 2، مما يتسبب في قيام مكون `OnboardingForm` الرئيسي لدينا بتصيير مكون `Step2`.
الخطوة 2: التحقق التدريجي لتفاصيل الشركة
جمال هذا النهج هو أن الحالة من الخطوة 1 يتم نقلها تلقائيًا. نحتاج فقط إلى تصييرها في حقول مخفية بحيث يتم تضمينها في إرسال النموذج التالي.
// Step2.jsx component
{state.errors.companyName} {state.errors.role}
export function Step2({ state }) {
return (
الخطوة 2: تفاصيل الشركة
{/* الاحتفاظ بالبيانات من الخطوة السابقة */}
{state.errors?.companyName &&
{state.errors?.role &&
);
}
ونقوم بتحديث إجراء الخادم للتعامل مع الخطوة 2.
// actions.js (updated)
// ...
if (step === 2) {
const validatedFields = schema.pick({ companyName: true, role: true }).safeParse({ companyName, role });
if (!validatedFields.success) {
return {
...currentState,
step: 2,
errors: validatedFields.error.flatten().fieldErrors,
};
}
// نجاح، انتقل إلى المراجعة النهائية
return {
...currentState,
step: 3,
errors: {},
};
}
// ...
المنطق متطابق مع الخطوة 1، لكنه يستهدف الحقول الخاصة بالخطوة 2. يدير خطاف `useFormState` الانتقال بسلاسة، محتفظًا بجميع البيانات وموفرًا تدفق تحقق نظيفًا وتدريجيًا.
الخطوة 3: المراجعة النهائية والإرسال
في الخطوة النهائية، نعرض جميع البيانات المجمعة للمستخدم لمراجعتها. سيؤدي الإرسال النهائي إلى تشغيل تحقق شامل لجميع الحقول قبل أن نلتزم بالبيانات في قاعدة البيانات.
// Step3.jsx component
{state.message} {state.message}
export function Step3({ state }) {
return (
الخطوة 3: تأكيد التفاصيل
{state.message && state.message.startsWith('Success') &&
{state.message && state.message.startsWith('Error') &&
);
}
يقوم منطق إجراء الخادم النهائي بإجراء تحقق كامل ومنطق العمل النهائي.
// actions.js (final version)
// ...
if (step === 3) {
// التحقق النهائي والكامل
const validatedFields = schema.safeParse({ name, email, companyName, role });
if (!validatedFields.success) {
// لا ينبغي أن يحدث إذا كان التحقق خطوة بخطوة صحيحًا، ولكنه ضمان جيد
return {
...currentState,
step: 1, // أعد المستخدم إلى الخطوة الأولى مع الأخطاء
errors: validatedFields.error.flatten().fieldErrors,
message: 'خطأ: تم العثور على بيانات غير صالحة. يرجى المراجعة.'
};
}
try {
// console.log('جاري الإرسال إلى قاعدة البيانات:', validatedFields.data);
// await saveToDatabase(validatedFields.data);
return { message: 'نجاح! اكتمال عملية الإعداد.', step: 4 }; // خطوة نجاح نهائية
} catch (dbError) {
return { ...currentState, step: 3, message: 'خطأ: تعذر حفظ البيانات.' };
}
}
// ...
بهذا، لدينا نموذج متعدد المراحل كامل وقوي، مع تحقق تدريجي وموثوق من جانب الخادم، وكل ذلك منظم بدقة بواسطة خطاف `useFormState`.
استراتيجيات متقدمة لتجربة مستخدم عالمية المستوى
بناء نموذج وظيفي شيء؛ جعله ممتعًا للاستخدام شيء آخر. إليك بعض التقنيات المتقدمة للارتقاء بنماذجك متعددة المراحل.
إدارة التنقل: التحرك ذهابًا وإيابًا
منطقنا الحالي يتحرك فقط للأمام. للسماح للمستخدمين بالعودة، لا يمكننا استخدام زر `type="submit"` بسيط. بدلاً من ذلك، سنقوم بإدارة الخطوة في حالة مكون جانب العميل واستخدام إجراء النموذج للتقدم للأمام فقط. ومع ذلك، فإن نهجًا أبسط يلتزم بالنموذج المرتكز على الخادم هو وجود زر "رجوع" (Back) يقوم أيضًا بإرسال النموذج ولكن بنية مختلفة.
// في مكون الخطوة...
// في إجراء الخادم...
const intent = formData.get('intent');
if (intent === 'back') {
return { ...currentState, step: step - 1, errors: {} };
}
توفير ملاحظات فورية باستخدام `useFormStatus`
يوفر خطاف `useFormStatus` حالة الانتظار (pending state) لإرسال النموذج ضمن نفس `
// SubmitButton.jsx
'use client';
import { useFormStatus } from 'react-dom';
export function SubmitButton({ text }) {
const { pending } = useFormStatus();
return (
{pending ? 'جاري الإرسال...' : text}
);
}
يمكنك بعد ذلك استخدام
هيكلة إجراء الخادم الخاص بك لقابلية التوسع
مع نمو نموذجك، يمكن أن تصبح سلسلة `if/else if` في إجراء الخادم غير عملية. يوصى باستخدام عبارة `switch` أو نمط أكثر نمطية لتنظيم أفضل.
// actions.js مع عبارة switch
switch (step) {
case 1:
// معالجة التحقق من صحة الخطوة 1
break;
case 2:
// معالجة التحقق من صحة الخطوة 2
break;
// ... إلخ
}
إمكانية الوصول (a11y) غير قابلة للتفاوض
للجمهور العالمي، إمكانية الوصول أمر لا بد منه. تأكد من أن نماذجك يمكن الوصول إليها من خلال:
- استخدام `aria-invalid="true"` في حقول الإدخال التي تحتوي على أخطاء.
- ربط رسائل الخطأ بالمدخلات باستخدام `aria-describedby`.
- إدارة التركيز بشكل مناسب بعد الإرسال، خاصة عند ظهور الأخطاء.
- ضمان أن جميع عناصر التحكم في النموذج قابلة للتنقل بلوحة المفاتيح.
منظور عالمي: التدويل و `useFormState`
إحدى المزايا الهامة للتحقق من الصحة القائم على الخادم هي سهولة التدويل (i18n). لم يعد يتعين ترميز رسائل التحقق من الصحة بشكل ثابت على العميل. يمكن لإجراء الخادم اكتشاف اللغة المفضلة للمستخدم (من رؤوس مثل `Accept-Language`، أو معلمة URL، أو إعداد ملف تعريف المستخدم) وإرجاع الأخطاء بلغته الأم.
على سبيل المثال، باستخدام مكتبة مثل `i18next` على الخادم:
// إجراء الخادم مع i18n
import { i18n } from 'your-i18n-config';
// ...
const t = await i18n.getFixedT(userLocale); // على سبيل المثال، 'es' للإسبانية
const schema = z.object({
email: z.string().email(t('errors.invalid_email')),
});
يضمن هذا النهج أن يتلقى المستخدمون في جميع أنحاء العالم ملاحظات واضحة ومفهومة، مما يحسن بشكل كبير من شمولية تطبيقك وسهولة استخدامه.
`useFormState` مقابل مكتبات جانب العميل: نظرة مقارنة
كيف يقارن هذا النمط بالمكتبات الراسخة مثل Formik أو React Hook Form؟ الأمر لا يتعلق بأيهما أفضل، بل أيهما مناسب للمهمة.
- مكتبات جانب العميل (Formik, React Hook Form): هذه ممتازة للنماذج المعقدة والتفاعلية للغاية حيث يكون رد الفعل الفوري من جانب العميل هو الأولوية القصوى. توفر مجموعات أدوات شاملة لإدارة حالة النموذج، والتحقق من الصحة، والإرسال بالكامل داخل المتصفح. قد يكون التحدي الرئيسي لها هو تكرار منطق التحقق من الصحة بين العميل والخادم.
- `useFormState` مع إجراءات الخادم: يتفوق هذا النهج حيث يكون الخادم هو المصدر النهائي للحقيقة. إنه يبسط البنية الكلية عن طريق مركزية المنطق، ويضمن سلامة البيانات، ويعمل بسلاسة مع التحسين التدريجي. المقايضة هي رحلة ذهاب وعودة عبر الشبكة للتحقق من الصحة، على الرغم من أن هذا غالبًا ما يكون ضئيلًا مع البنية التحتية الحديثة.
بالنسبة للنماذج متعددة المراحل التي تتضمن منطق عمل أو بيانات مهمة يجب التحقق من صحتها مقابل قاعدة بيانات (على سبيل المثال، التحقق مما إذا كان اسم المستخدم مأخوذًا)، يقدم نمط `useFormState` بنية أكثر مباشرة وأقل عرضة للأخطاء.
الخاتمة: مستقبل النماذج في React
خطاف `useFormState` هو أكثر من مجرد واجهة برمجة تطبيقات جديدة؛ إنه يمثل تحولًا فلسفيًا في كيفية بناء النماذج في React. من خلال تبني نموذج مرتكز على الخادم، يمكننا إنشاء نماذج متعددة المراحل تكون أكثر قوة وأمانًا ويمكن الوصول إليها وأسهل في الصيانة. يلغي هذا النمط فئات كاملة من الأخطاء المتعلقة بمزامنة الحالة ويوفر هيكلًا واضحًا وقابلاً للتوسع للتعامل مع تدفقات المستخدم المعقدة.
من خلال بناء محرك تحقق من الصحة باستخدام `useFormState`، فإنك لا تدير الحالة فحسب؛ بل تقوم بهندسة عملية جمع بيانات مرنة وسهلة الاستخدام ترتكز على مبادئ تطوير الويب الحديث. للمطورين الذين يبنون تطبيقات لجمهور متنوع وعالمي، يوفر هذا الخطاف القوي الأساس لإنشاء تجارب مستخدم عالمية المستوى حقًا.