إنشاء مكوّنات حقول قابلة لإعادة الاستخدام (Creating Reusable Field Components)
الدرس 17 من 21 · دورة TanStack Form الكاملة · 10 دقيقة و36 ثانية
الفيديو الأصلي باللغة الإنجليزية. إذا تعذّر تشغيله هنا، شاهده على موقع المؤلف. التفريغ النصي العربي الكامل أدناه.
مستودع شيفرة هذا الدرس
الفرع:
useAppForm-3
[00:01] بالتعريف بمكوّنٍ لحقلنا النصّي. وأمّا الحقول الأخرى، فسأفعلها أيضًا وأمرّ معك عليها سريعًا. لكن فلنبدأ بالحقل النصّي أو الـ text input. فإذًا في هنا، فلنذهب إلى
[00:15] مجلد components لدينا. فربما فلننشئ مجلدًا آخر. فمجلد ui هذا هو مكوّنات shadcn. فلننشئ آخر لمكوّناتنا نحن.
[00:26] لنسمّه ربما form.
[00:28] داخل form، فلننشئ مكوّنًا لحقل الإدخال النصّي الخاصّ بنا — الـ text input. ففلنسمّه text-input.tsx.
[00:37] والآن في هنا، فلنعرّف مكوّن React فحسب. وما يلزمنا فعله هو أن ننسخ الأشياء التي نقوم بها في نموذجنا لعرض الـ input. ففلنسخ كلّ ذلك من هنا.
[00:48] ولنقصّه أيضًا في الواقع. فسأقصّه من هنا، وأضعه في هنا فحسب هكذا. ونحتاج كذلك إلى استيراد أشيائنا. فلننسخه ربما من هنا.
[01:02] فلنستورد أشياءنا هكذا. فلا نحتاج إلى كلّ ذلك، فلنُزِل الاستيرادات غير المستخدمة.
[01:09] وفلنستورد أيضًا الـ input لدينا. وفلنحفظ. وسيواجهنا الآن هذه المشكلة حيث نحتاج إلى أن نملك الوصول إلى الحقل في هنا. فحين أنشأنا نموذجنا كنّا نملك الوصول إلى هذا الحقل في هذه الدالة، أليس كذلك؟ غير أنّنا
[01:20] كما ذكرنا، سنملك الوصول إليه عن طريق السياق (context). فإذًا في هنا في form-context.tsx، تذكّر أنّنا كنّا نُصدّر fieldContext وuseFieldContext ونمرّر fieldContext هذا إلى الدالة التي
[01:35] تنشئ useAppForm هنا أو هنا. فإذا استخدمنا useAppForm الآن في نموذجنا — ونحن نستخدم useAppForm — فحين ننشئ مكوّنًا لحقلٍ سيكون بذلك الحقل السياقُ fieldContext متوفّرًا. فإذًا ببساطة نستخدم
[01:55] دالة useFieldContext لنملك الوصول إلى الحقل. فإذًا في هنا نستطيع أن نستدعي useFieldContext. فتأكّد فقط أنّ هذه هي دالة useFieldContext التي صدّرتها من ملف form-context هذا. أليس كذلك؟ و
[02:12] يلزمنا فقط أن نعطيه نوعًا. فإذًا هذا مربّع نصّ، فسيكون حقلنا من نوع string.
[02:18] وبفعلنا هذا، أظنّ أنّه لن نرى أيّ أخطاء. غير أنّ بعض الأشياء عندنا هنا مكتوبة يدويًّا: فالـ label، والـ description، والـ placeholder، وهلمّ جرًا. فالذي يلزمنا هو أن نتمكّن من تغيير
[02:32] هذه حين نستخدم مكوّننا. فببساطة نمرّرها كخصائص (props). ففلنعرِّف خصائصنا هنا: فالـ label، والـ description. وتذكّر أيضًا أنّ لدينا هذا id الذي نمرّره في هذه وفي هذه.
[02:45] فنحتاج إلى أن نجعلها خاصيةً أيضًا.
[02:47] وفلنعطيها أيضًا نوعًا. فالـ label سيكون من نوع string، والـ description أيضًا، ولنجعله string اختياريًّا. فالآن نستطيع ببساطة أن نزيله ونستبدله بـ label.
[03:01] وفلنجعل الـ placeholder يماثل الـ label. ويمكنك أن تصنع له prop خاصًّا، لكن فلنُبق الأمر ببساطة.
[03:08] وفي هذا وهذا يلزمنا تمرير الـ id.
[03:12] ويلزمنا في هذا أن نمرّر description لدينا هكذا — عذرًا — description.
[03:20] وفي هذا نُصيّر الأخطاء فحسب. وأظنّ أنّنا لن نحتاج إلى تغيير شيء هنا. والآن بامتلاكنا هذا المكوّن نستطيع أن نذهب إلى form.tsx ونمرّره هنا في fieldComponents لدينا كي نملك الوصول إليه في
[03:33] نموذجنا وأن نستخدمه، بدلًا من الاضطرار إلى كتابة كلّ هذه الشيفرة القالبية (boilerplate) لكلّ حقل. فإذًا في هنا نستطيع أن نمرّر مكوّنًا، ويمكنك أن تسمّيته أيّ شيء. سأتّجه إلى text input وأمرّر مكوّن مربّع النص
[03:49] الذي أنشأناه للتوّ — وتأكّد أنّك تستورد المكوّن الصحيح، المكوّن الداخليّ في مجلد forms — عذرًا — مجلد form الذي أنشأناه داخل components — هكذا. وأخيرًا نستطيع أن نعود إلى نموذجنا، وتذكّر أنّنا
[04:06] أزلنا الأشياء التي كنّا نعيدها للاسم الأول (firstName)، والآن نستطيع ببساطة أن نعيد الحقل، ثم داخل الحقل سنحصل على الوصول إلى مكوّن مربّع النص الذي مرّرناه هنا عندما عرّفنا خُطّافنا. غير أنّنا
[04:22] سنحتاج إلى أن نعدّل شيئًا قليلًا هنا.
[04:24] فإذًا الآن، بدلًا من استخدام form.Field، نحتاج إلى استخدام form.AppField. ولماذا؟ لأنّ هذا سيتيح السياقَ اللازم لمكوّن مربّع النص لدينا — السياق الذي نستخدمه هنا. فإذًا يلزمنا تغيير هذا من form.Field
[04:41] إلى form.AppField ثم نغلقه هكذا.
[04:45] والآن سنحصل على الوصول إلى مكوّناتنا. وفي حالتنا لدينا مربّع النص، حيث يلزمنا فحسب تمرير... عذرًا... الـ id. فلنقل firstName، والـ label، ولدينا أيضًا description اختياريّ أيضًا. وبـ
[05:03] المناسبة، أظنّ أنّه يلزمنا في هنا أن نتحقّق من أنّ لدينا description قبل أن نعرض — عذرًا — قبل أن نعرض مكوّن وصف الحقل (FieldDescription).
[05:15] فإذًا إذا حفظنا هذا، فسنحصل على الاسم الأول. لكن كما ترى، هذا الآن أصغر بكثير مما كان عندنا سابقًا. فلسنا بحاجة إلى تمرير أيّ قيم، أو أيّ نداءات (callbacks)، وإنّما هذا المكوّن البسيط للحقل فحسب. وإذا شُئنا، فسيتيسّر
[05:28] من تمرير description أيضًا. وينبغي أن يظهر هكذا.
[05:34] والآن نستطيع ببساطة أن نفعل الشيء نفسه مع بقيّة حقولنا. فدعنا نفعل ذلك مع الاسم الأخير أيضًا مثلاً. فتذكّر أنّه إذا كان لنا الاسم الأول، فعندنا حقل الاسم الأخير هذا. فإذًا في هنا، كما ترى، هذا هو مقدار
[05:50] الشيفرة التي يمكننا إزالتها الآن. فدعنا نزِل هذا وببساطة — عذرًا — دعنا نزِل من هنا، من الحقل إلى هنا، هكذا وببساطة نغيّر هذا
[06:06] إلى AppField، وفي هنا نستخدم فحسب field.TextInput ونمرّر أشياءنا: أي الـ id، والـ label، وdescription اختياريّ أيضًا.
[06:24] فإذًا — ممتاز. كما ترى، يمكن أن تكون نماذجنا أصغر حجمًا بكثير. والآن، إذا فعلنا الشيء نفسه مع بقية حقولنا، فتذكّر أنّه كانت لدينا بعض الأشياء التي كنّا نجرّبها هنا؛ فهذه validators مثلًا،
[06:37] فلنتخلَّ عنها، إذ بدلًا من امتلاء المكان بالضجيج هنا، نزيلها والـ listeners أيضًا من هنا هكذا.
[06:46] وتذكّر أيضًا أنّنا كنّا نستخدم form.subscribe هنا كي نعرض الاسم الأخير فقط إذا كان لدينا اسم أول، وهذا كان مجرد مثال أيضًا. فدعنا نزِله ربما. فلنزِل هذه ونُبقِ نموذجنا فقط هكذا — عذرًا — نُبقِ
[07:02] حقلنا، عذرًا. والآن ما سأفعله هو أنّني سأفعل الشيء نفسه مع بقيّة حقولنا. فسأنشئ مكوّنًا للقائمة المنسدلة (dropdown)، ومكوّنًا للـ checkbox، وهلمّ جرًا.
[07:14] وسأستخدمها هنا كذلك وأمرّ معك على النتيجة. ولن أفعل هذا على الشاشة، فأظنّ أنّه سيكون بلا فائدة؛ إذ ستكون العملية هي نفسها: إنشاء مكوّناتَ واستخدامها هنا. فإذًا الآن
[07:26] بعد أن أنتهي من إنشاء مكوّناتٍ لمدخلاتنا وتحديث نموذجنا واستخدام هذه المكوّنات، فكما ترى، ملفنا — نموذج التسجيل — صار حجمه الآن نصف الحجم تقريبًا، ويمكننا بل أن نفعل الأفضل. سنرى
[07:41] ذلك لاحقًا، لكن في الوقت الحالي هذا أفضل بكثير. فما فعله هو أنّني دخلت مجلد components لدينا. وفي داخل مجلد form هذا أنشأت مكوّنًا للـ checkbox، ومكوّنًا للـ select أيضًا. وبخصوص مربّع النص فقد
[07:59] عدّلته قليلًا فحسب كي يوافق نوعًا (type) أيضًا؛ وهو افتراضيًّا من نوع text، لكن يمكننا أن نلغي هذا النوع كي نستخدمه لحقل كلمة السرّ أيضًا. وقيل أنّني أنشأت مكوّن الـ checkbox — في
[08:11] الطريقة نفسها. وبالطبع سأزوّدك بشيفرة هذا، وقد أدركت للتوّ أنّه هنا — لا أدري كيف حدث هذا. غير أنّه فلنصلحه سريعًا.
[08:22] فنحتاج أيضًا إلى أن نمرّر ربما id هنا لمكوّننا، ثم نعطي id للـ checkbox لدينا، وفي htmlFor هنا نمرّر الـ id هكذا.
[08:37] عذرًا عن هذا. لكن عمومًا، المفهوم هو نفسه: ننشئ الـ checkbox، ونمرّر له أشياءنا، ونحصل على الوصول إلى الحقل باستخدام useFieldContext، ونُظهر أخطاءنا أيضًا. وينبغي أن يُدعى هذا checkbox. يبدو أنني
[08:50] كنت مستعجلًا حين أنشأت هذه المكوّنات. وفلنسمّه أيضًا شيئًا آخر لأنّنا نستورد الـ checkbox من shadcn أصلًا.
[09:00] لكن عمومًا، الآن أيضًا مكوّن الـ select. أنجزته بالطريقة نفسها، مستخدمًا مكوّنات shadcn فقط، وناقلًا الخصائص التي نحتاجها، وعاملًا الأخطاء وما إلى ذلك. وبعد ذلك في نموذجنا يلزمنا تمرير هذه
[09:16] المكوّنات إلى createFormHook كي نحصل، حين نستخدم useAppForm، على الوصول إليها، وبفعلنا ذلك استخدمتُ هذه المكوّنات في نموذجنا لكلّ حقولنا. فدعنا نمرّ عليها
[09:30] بسرعة. فلدينا الاسم الأول الآن، وهو أصغر بكثير، والاسم الأخير، والعنوان كذلك. تذكّر فقط أنّك ستحتاج إلى تغليف مكوّناتك بـ form.AppField بدلًا من form.Field، وكذا الحال مع كلمة السرّ. فهي العمل نفسه:
[09:47] مرّر النوع password هنا فحسب.
[09:48] تأكيد كلمة السرّ. وبالنسبة إلى مصفوفة المهارات فقد غيّرتُ تنسيقها قليلًا، لكنّها تقريبًا كما كانت. لكني أستخدم الآن — بالطبع، لا تنسَ form.AppField هنا وهنا — أستخدم أيضًا مكوّن الـ select
[10:05] الذي عرّفتُه هنا أيضًا.
[10:10] وأخيرًا بالنسبة إلى الـ checkbox فكما ترى أصغر بكثير، ونحتاج فقط إلى تمرير الـ id الذي أصلحناه للتوّ.
[10:19] ففلنقل accept terms، وفي هنا لدينا form.subscribe والأزرار. لم أفعل في هنا أيّ شيء، فينبغي أن يعمل بالطريقة نفسها تمامًا. فإذا أرسلنا، فسنحصل على التحقّق من الصحة وكلّ شيء. لكن نموذجنا الآن أصبح
[10:33] أصغر حجمًا وأسهل استخدامًا كذلك. غير أنّه ما زال لدينا القدرة على فعل ما هو أفضل.