بناء النماذج التفاعلية في React بكفاءة عالية باستخدام مكتبة react-hook-form

دقائق القراءة: 12
في هذا المقال، سنتعمق في استكشاف مكتبة react-hook-form، وهي أداة قوية وفعالة لبناء النماذج في تطبيقات React. ستتعلم كيفية استخدام هذه المكتبة ودمجها بسلاسة مع مشاريع React الخاصة بك. كما سنستعرض الأسباب التي جعلتها خيارًا شائعًا بشكل متزايد لتطوير النماذج، سواء كانت بسيطة أو معقدة، مع دعم متقدم للتعامل مع عمليات التحقق من الصحة (validations) المعقدة. لنبدأ رحلتنا!

يُعد التعامل مع النماذج (forms) في React مهمة قد تكون معقدة، وتزداد هذه التعقيدات كلما زاد عدد حقول الإدخال (input fields) ومتطلبات التحقق من صحتها. لنلقِ نظرة على مثال بسيط يوضح ذلك:

 import React, { useState } from "react" ;
 import "./styles.css" ;

 export default function App ( ) {
   const [state, setState] = useState({
     email : "" ,
     password : ""
   });

   const handleInputChange = ( event ) => {
     setState( ( prevProps ) => ({
       ...prevProps,
       [event.target.name]: event.target.value
     }));
   };

   const handleSubmit = ( event ) => {
     event.preventDefault();
     console .log(state);
   };

   return (
     < div className = "App" >
       < form onSubmit = {handleSubmit} >
         < div className = "form-control" >
           < label > Email </ label >
           < input type = "text" name = "email" value = {state.email} onChange = {handleInputChange} />
         </ div >
         < div className = "form-control" >
           < label > Password </ label >
           < input type = "password" name = "password" value = {state.password} onChange = {handleInputChange} />
         </ div >
         < div className = "form-control" >
           < label > </ label >
           < button type = "submit" > Login </ button >
         </ div >
       </ form >
     </ div >
   );
 }

يمكنك مشاهدة عرض توضيحي لهذا الكود على Code Sandbox عبر الرابط التالي: نموذج تسجيل الدخول.

في الكود أعلاه، لدينا حقلان إدخال فقط: email و password، بالإضافة إلى زر إرسال. كل حقل إدخال يتطلب خاصيتي value و onChange لتحديث حالة المكون بناءً على مدخلات المستخدم. كما أضفنا دالة handleSubmit التي تعرض البيانات المدخلة في النموذج إلى وحدة التحكم (console). يبدو هذا جيدًا للوهلة الأولى.

ولكن ماذا لو احتجنا إلى إضافة تحققات مثل التحقق من أن الحقل مطلوب (required field validation)، أو التحقق من الحد الأدنى للطول، أو تحققات معقدة لكلمة المرور والبريد الإلكتروني، وعرض رسائل الخطأ المناسبة؟ سيصبح الكود أكثر تعقيدًا وطولًا مع زيادة عدد حقول الإدخال والتحققات المرتبطة بها. هذا متطلب شائع جدًا في أي تطبيق ويب. لتسهيل التعامل مع النماذج، تتوفر العديد من المكتبات مثل Formik، redux-form، react-final-form، و react-hook-form. ولكن المكتبة التي تكتسب شعبية كبيرة هي react-hook-form. لذا، دعنا نتعلم الآن لماذا وكيف نستخدمها.

التحضير للمشروع: إنشاء تطبيق React جديد

للبدء، سنقوم بإنشاء تطبيق React جديد. نفّذ الأمر التالي في الطرفية (terminal):

npx create-react-app react-hook-form-demo

بعد إنشاء المشروع، احذف جميع الملفات من مجلد src وأنشئ ملفين جديدين: index.js و styles.css داخل مجلد src. لتثبيت مكتبة النماذج، نفّذ الأمر التالي في الطرفية:

yarn add react-hook-form

إنشاء الصفحات الأولية

افتح ملف src/index.js وأضف المحتوى التالي بداخله:

 import React from 'react' ;
 import ReactDOM from 'react-dom' ;
 import App from './App' ;

 ReactDOM.render(
   < App /> ,
   document .getElementById( 'root' ));

افتح ملف src/styles.css وأضف إليه التنسيقات الضرورية التي ستوفر المظهر الجمالي للنموذج. الآن، أنشئ ملفًا جديدًا باسم App.js داخل مجلد src بالمحتوى التالي:

 import React from "react" ;
 import "./styles.css" ;

 export default function App ( ) {
   return (
     < div className = "App" >
       < form >
         < div className = "form-control" >
           < label > Email </ label >
           < input type = "text" name = "email" />
         </ div >
         < div className = "form-control" >
           < label > Password </ label >
           < input type = "password" name = "password" />
         </ div >
         < div className = "form-control" >
           < label > </ label >
           < button type = "submit" > Login </ button >
         </ div >
       </ form >
     </ div >
   );
 }

هنا، أضفنا فقط حقلي البريد الإلكتروني وكلمة المرور إلى النموذج.

بناء نموذج أساسي باستخدام مكتبة react-hook-form

توفر مكتبة react-hook-form خطافًا (hook) باسم useForm يمكننا استخدامه للتعامل مع النماذج. قم باستيراد الخطاف useForm على النحو التالي:

 import { useForm } from 'react-hook-form' ; 

استخدم الخطاف useForm بهذه الطريقة:

 const { register, handleSubmit, errors } = useForm(); 

هنا، تمثل المتغيرات التالية وظائف وخصائص أساسية:

  • register: هي دالة تُستخدم كمرجع (ref) توفره الدالة useForm. يمكننا تعيينها لكل حقل إدخال لتمكين react-hook-form من تتبع التغييرات في قيمة حقل الإدخال.
  • handleSubmit: هي الدالة التي نستدعيها عند إرسال النموذج.
  • errors: ستحتوي على أخطاء التحقق من الصحة (validation errors)، إن وجدت.

الآن، استبدل محتويات ملف App.js بالمحتوى التالي:

 import React from "react" ;
 import { useForm } from "react-hook-form" ;
 import "./styles.css" ;

 export default function App ( ) {
   const { register, handleSubmit, errors } = useForm();

   const onSubmit = ( data ) => {
     console .log(data);
   };

   return (
     < div className = "App" >
       < form onSubmit = {handleSubmit(onSubmit)} >
         < div className = "form-control" >
           < label > Email </ label >
           < input type = "text" name = "email" ref = {register} />
         </ div >
         < div className = "form-control" >
           < label > Password </ label >
           < input type = "password" name = "password" ref = {register} />
         </ div >
         < div className = "form-control" >
           < label > </ label >
           < button type = "submit" > Login </ button >
         </ div >
       </ form >
     </ div >
   );
 }

في الكود أعلاه، قمنا بتعيين مرجع (ref) لكل حقل إدخال، وهو ما حصلنا عليه من الخطاف useForm:

ref={register}

كما أضفنا الدالة onSubmit التي يتم تمريرها إلى الدالة handleSubmit:

<form onSubmit={handleSubmit(onSubmit)}>

لاحظ أنه يجب إعطاء كل حقل إدخال اسمًا فريدًا (unique name)، وهو أمر إلزامي لكي تتمكن مكتبة react-hook-form من تتبع البيانات المتغيرة. عند إرسال النموذج، ستتولى الدالة handleSubmit عملية الإرسال. ستقوم بإرسال البيانات المدخلة من قبل المستخدم إلى الدالة onSubmit التي نقوم بتسجيلها في وحدة التحكم (console):

 const onSubmit = ( data ) => {
   console .log(data);
 }; 

الآن، ابدأ تشغيل التطبيق عن طريق تنفيذ الأمر yarn start.
نموذج تسجيل دخول بسيط في React يعرض البيانات في الكونسول
كما ترى، عند إرسال النموذج، يتم عرض التفاصيل المدخلة من قبل المستخدم في وحدة التحكم. مقارنةً بالكود الذي لا يستخدم react-hook-form (والذي رأيناه في بداية هذا المقال)، فإن هذا الكود أبسط بكثير. هذا لأننا لا نحتاج إلى إضافة خاصيتي value و onChange لكل حقل إدخال، ولا توجد حاجة لإدارة حالة التطبيق بأنفسنا.

إضافة تحققات (Validations) إلى النموذج

الآن، دعنا نضيف تحققات الحقل المطلوب (required field) والحد الأدنى للطول (minimum length) لحقول الإدخال. لإضافة التحقق، يمكننا تمريره إلى دالة register التي يتم تمريرها كمرجع (ref) لكل حقل إدخال على النحو التالي:

 <input type= "text" name= "email" ref={register({ required : true })} />
 < input type = "password" name = "password" ref = {register({ required: true , minLength: 6 })} /> 

نريد أيضًا عرض رسالة خطأ إذا فشل التحقق. عندما يفشل التحقق، سيتم ملء الكائن errors القادم من useForm بالحقول التي فشل فيها التحقق. افتح ملف App.js واستبدل محتوياته بالمحتوى التالي:

 import React from "react" ;
 import { useForm } from "react-hook-form" ;
 import "./styles.css" ;

 export default function App ( ) {
   const { register, handleSubmit, errors } = useForm();

   const onSubmit = ( data ) => {
     console .log(data);
   };

   return (
     < div className = "App" >
       < form onSubmit = {handleSubmit(onSubmit)} >
         < div className = "form-control " >
           < label > Email </ label >
           < input type = "text" name = "email" ref = {register({ required: true , pattern: /^[^@ ]+@[^@ ]+\.[^@ .]{2,}$/ })} />
           {errors.email && errors.email.type === "required" && (
             < p className = "errorMsg" > Email is required. </ p >
           )}
           {errors.email && errors.email.type === "pattern" && (
             < p className = "errorMsg" > Email is not valid. </ p >
           )}
         </ div >
         < div className = "form-control" >
           < label > Password </ label >
           < input type = "password" name = "password" ref = {register({ required: true , minLength: 6 })} />
           {errors.password && errors.password.type === "required" && (
             < p className = "errorMsg" > Password is required. </ p >
           )}
           {errors.password && errors.password.type === "minLength" && (
             < p className = "errorMsg" > Password should be at-least 6 characters. </ p >
           )}
         </ div >
         < div className = "form-control" >
           < label > </ label >
           < button type = "submit" > Login </ button >
         </ div >
       </ form >
     </ div >
   );
 }

نموذج React مع تحققات البريد الإلكتروني وكلمة المرور ورسائل الخطأ
هنا، لحقل إدخال البريد الإلكتروني، قدمنا تحققات الحقل المطلوب (required) ومطابقة النمط (pattern matching):

<input type= "text" name= "email" ref={register({ required : true , pattern : /^[^@ ]+@[^@ ]+\.[^@ .]{2,}$/ })} />

وبالتالي، عند الكتابة في حقل إدخال البريد الإلكتروني، سيتم تشغيل التحقق بمجرد إرسال النموذج. إذا فشل التحقق، فسيتم ملء الحقل errors.email داخل الكائن errors بحقل type الذي استخدمناه لعرض رسالة الخطأ:

 {errors.email && errors.email.type === "required" && (
   < p className = "errorMsg" > Email is required. </ p >
 )} 

وبطريقة مماثلة، أضفنا التحقق لحقل كلمة المرور. كما ترون، يتم التركيز تلقائيًا على كل حقل إدخال إذا كان هناك أي خطأ في التحقق لهذا الحقل عند إرسال النموذج. أيضًا، لا يتم إرسال النموذج طالما توجد أخطاء في التحقق. يمكنك ملاحظة أن عبارة console.log لا تُطبع إلا إذا كان النموذج صالحًا.

لقد قللت مكتبة react-hook-form من كمية الكود الذي يتعين علينا كتابته. كما أن التحقق سريع الاستجابة، فبمجرد أن يصبح الحقل صالحًا، تختفي رسالة الخطأ على الفور. ولكن مع زيادة عدد التحققات لكل حقل، ستظل الشروط البرمجية (conditional checks) وكود رسائل الخطأ تزداد. لذا، يمكننا إعادة هيكلة الكود لجعله أبسط.

تبسيط كود التحقق من الصحة

لنلقِ نظرة على الكود التالي بعد إعادة الهيكلة:

 import React from 'react' ;
 import { useForm } from 'react-hook-form' ;
 import './styles.css' ;

 export default function App ( ) {
   const { register, handleSubmit, errors } = useForm();

   const onSubmit = ( data ) => {
     console .log(data);
   };

   return (
     < div className = "App" >
       < form onSubmit = {handleSubmit(onSubmit)} >
         < div className = "form-control " >
           < label > Email </ label >
           < input type = "text" name = "email" ref = {register({
             required: ' Email is required. ',
             pattern: {
               value: /^[^@ ]+@[^@ ]+\.[^@ .]{2,}$/,
               message: ' Email is not valid. '
             }
           })} />
           {errors.email && < p className = "errorMsg" > {errors.email.message} </ p > }
         </ div >
         < div className = "form-control" >
           < label > Password </ label >
           < input type = "password" name = "password" ref = {register({
             required: ' Password is required. ',
             minLength: {
               value: 6 ,
               message: ' Password should be at-least 6 characters. '
             }
           })} />
           {errors.password && (
             < p className = "errorMsg" > {errors.password.message} </ p >
           )}
         </ div >
         < div className = "form-control" >
           < label > </ label >
           < button type = "submit" > Login </ button >
         </ div >
       </ form >
     </ div >
   );
 }

في الكود أعلاه، قمنا بتغيير كود التحقق من البريد الإلكتروني وكلمة المرور. لحقل إدخال البريد الإلكتروني، قمنا بتغيير الكود السابق:

 <input type= "text" name= "email" ref={register({ required : true , pattern : /^[^@ ]+@[^@ ]+\.[^@ .]{2,}$/ })} /> 

إلى الكود الجديد التالي:

 <input type= "text" name= "email" ref={register({ required : 'Email is required.' , pattern : { value : /^[^@ ]+@[^@ ]+\.[^@ .]{2,}$/ , message: 'Email is not valid.' } })} /> 

هنا، قدمنا رسالة الخطأ التي نريد عرضها مباشرة عند إضافة التحقق نفسه. لذلك لم نعد بحاجة إلى إضافة تحققات إضافية لكل نوع من التحقق. نقوم بعرض رسالة الخطأ باستخدام خاصية message المتاحة داخل الكائن errors لكل حقل إدخال:

{errors.email && < p className = "errorMsg" > {errors.email.message} </ p > }

بهذه الطريقة، تم تبسيط الكود بشكل أكبر، مما يسهل إضافة تحققات إضافية في المستقبل. لاحظ أنه إذا كانت هناك أخطاء في التحقق، فلن يتم تنفيذ معالج onSubmit، وسيتم التركيز تلقائيًا على حقل الإدخال المقابل (وهو أمر مفيد جدًا).

إضافة طريقة تحقق مخصصة (Custom Validation)

يمكنك حتى توفير تحقق مخصص لحقل الإدخال عن طريق إضافة دالة validate. هذا مفيد إذا كنت بحاجة إلى إجراء تحققات معقدة مثل هذا المثال:

 // validation function
 const validatePassword = ( value ) => {
   if (value.length < 6 ) {
     return 'Password should be at-least 6 characters.' ;
   } else if ( ! /(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s)(?=.*[!@#$*])/ .test(value) ) {
     return 'Password should contain at least one uppercase letter, lowercase letter, digit, and special symbol.' ;
   }
   return true ;
 };

 // JSX
 < input type = "password" name = "password" ref = {register({ required: ' Password is required. ', validate: validatePassword })} /> 

نموذج React مع تحقق مخصص لكلمة المرور يعرض رسالة خطأ معقدة
الآن أنت تعرف كيفية استخدام react-hook-form لإنشاء النماذج في React بالإضافة إلى التحققات المعقدة.

لماذا تتفوق react-hook-form على البدائل الأخرى؟

دعنا نستعرض بعض الأسباب الإضافية التي تجعل react-hook-form خيارك المفضل للتعامل مع النماذج:

  • **تعقيد الكود أقل:** مقارنةً بـ Formik و redux-form والبدائل الأخرى، تتميز react-hook-form بكود أقل تعقيدًا.
  • **التكامل السلس:** تتكامل react-hook-form بشكل ممتاز مع مكتبة yup للتحقق من المخططات (schema validation)، مما يتيح لك دمج مخططات التحقق الخاصة بك بسهولة.
  • **عدد أقل من عمليات إعادة التصيير (Re-renders):** يقل عدد عمليات إعادة التصيير في التطبيق بشكل ملحوظ مقارنة بالبدائل، مما يحسن الأداء.
  • **وقت تحميل أسرع (Mounting time):** تتميز بوقت تحميل أقل مقارنة بالبدائل.

للاطلاع على مقاييس المقارنة الفعلية، يمكنك البحث عن المزيد من التفاصيل في المصادر الموثوقة.

الخلاصة التقنية

في هذا المقال، استعرضنا كيفية استخدام مكتبة react-hook-form، ولماذا أصبحت الخيار المفضل للعديد من المطورين لبناء نماذج بسيطة ومعقدة في React. لقد أثبتت هذه المكتبة قدرتها على تبسيط عملية إدارة النماذج والتحقق من الصحة بشكل كبير، مما يقلل من تعقيد الكود ويزيد من كفاءة التطبيق. من خلال استخدام الخطافات (hooks) الحديثة في React، توفر react-hook-form تجربة تطوير سلسة وأداءً ممتازًا بفضل تقليل عمليات إعادة التصيير، مما يجعلها أداة لا غنى عنها في أي مشروع React يتطلب نماذج قوية ومرنة.

يمكنك العثور على الكود المصدري لهذا التطبيق على GitHub عبر الرابط التالي: الكود المصدري على GitHub.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *