دورة TanStack Form الكاملة › دورة TanStack Form الكاملة 1 / 1 السابق

إنشاء مجموعة حقول قابلة لإعادة الاستخدام باستخدام withFieldGroup HOC (Creating Reusable Group of Fields with withFieldGroup HOC)

الدرس 21 من 21 · دورة TanStack Form الكاملة · 11 دقيقة و58 ثانية

الفيديو الأصلي باللغة الإنجليزية. إذا تعذّر تشغيله هنا، شاهده على موقع المؤلف. التفريغ النصي العربي الكامل أدناه.

مستودع شيفرة هذا الدرس

الفرع: withFieldGroup

[00:01] withForm تمكّنّا من تقسيم نموذجنا إلى أجزاء أصغر. غير أنّه هذه الأجزاء الصغيرة التي أنشأناها لا بدّ أن تكون ابنًا لنموذجنا الرئيسي. فأنشأنا RegisterSkills هنا، وRegisterSkills لا يمكن أن تُستخدم سوى بوصفها ابنًا لنموذجنا الرئيسي

[00:16] الذي نعرّفه هنا. فلاحظ إذًا أنّنا نمرّر الخيارات نفسها — registerFormOpts — عندما نستخدم useAppForm وكذلك عندما نستخدم withForm هكذا. فهذان المكوّنان مربوطان ببعضهما.

[00:30] فRegisterSkills ملزمةٌ أن تُستخدم داخل النموذج نفسه الذي عرّفناه هنا. وهذا جيد، فربما هذا هو ما تحتاجه.

[00:37] غير أنّه في بعض الأحيان قد يلزمنك فعل شيءٍ مماثل، غير أنّه بدلًا من أن تنشئ مكوّنًا مربوطًا بنموذجٍ واحد، مثل مكوّن RegisterSkills هذا، فربما تودّ أن تنشئ مجموعةً من الحقول قابلةً لإعادة الاستخدام (reusable group of fields)

[00:49] يمكن استخدامها في أيّ نموذج، لا في نموذجٍ محدَّد فقط. فمثلًا، لدينا هنا password وconfirmPassword. فربما سيُستخدمان هذان الحقلان، اه، هنا في نموذج تسجيل (register form)، وربما كذلك أيضًا في نموذج تغيير

[01:05] كلمة السر وهلمّ جرا. فسيلزمنا إذًا — بدلًا من أن يلزمنا إنشاء هذه الحقول لكلّ نموذجٍ من النماذج — أن تكون هذه محدَّدة مرةً واحدةً بطريقةٍ ما، ثم أن نتمكن من إعادة استخدام هذه الحقول، اه، في نماذج متعددة.

[01:18] يمكننا فعل ذلك بهذا التصدير (export) الآخر الذي لدينا هنا في form.tsx وهو withFieldGroup. فلنرَ إذًا كيف يمكننا استخدامه، ولنجعل من هذا مثالًا: أن نحاول نقل password وconfirmPassword إلى مكوّنٍ منفصل يمكن استخدامه في

[01:32] عدّة نماذج. فلنفعل ذلك بنقل هذه الحقول، اه، إلى مكوّنٍ منفصل. وكما أشرت، ما سنفعله الآن هو أننا سننشئ مكوّنًا يتضمّن password وconfirmPassword، ويمكن لهذا المكوّن أن

[01:45] يُستخدم ضمن أيّ نموذج. فهو إذًا غير مربوطٍ بنموذج التسجيل. فربما بدلًا من أن ننشئ هذا المكوّن داخل register form، يمكننا وضعه في مجلدٍ مشترك (shared folder) ما، ربما في components أو — اه — في الحقيقة فلنضعه داخل

[02:00] components ثم داخل form هنا.

[02:02] فلننشئ ملفًا جديدًا. فلنسمّه ربما PasswordFields.tsx.

[02:11] ولنرَ في هنا كيف سينشأ هذا المكوّن. ففي هنا فلنصدّر مكوّنًا جديدًا. فلنسمّه PasswordFields، وهذا المكوّن سيكون مُلفًّفًا داخل مكوّنٍ من

[02:22] رتبة أعلى يمكننا استيراده من خُطّافاتنا. وفي هنا، مماثلين لـ withForm، يلزمنا تمرير قيمنا الافتراضية (default values) — اه —

[02:30] غير أنّه خلافًا لـ withForm — اه — الذي استخدمناه، اه، هنا في RegisterSkills، فلن نمرّر خيارات register form نفسها لأنّ هذا لم يعد مربوطًا بنموذجٍ محدَّد الآن. فما يمكننا فعله إذًا هو أنّه يمكننا فقط أن

[02:44] نمرّر له defaultValues هكذا. وفي هنا سأعرّف بعض الأنواع (types) والقيم الافتراضية الخاصة بـ password وconfirmPassword لدينا. فنوعنا سيتضمّن password من نوع string وconfirmPassword من نوع string. وdefaultValues لدينا

[02:59] ستكون هي password وconfirmPassword بسلاسل فارغة هنا. وهذا ما سنمرّره هنا بوصفه قيمًا افتراضية لهذه المجموعات من الحقول. ولكن الآن، عذرًا، فلنزِل هذا كذلك أيضًا.

[03:12] ويمكننا الآن أيضًا تمرير مكوّن التصيير (render) لدينا مماثلين لـ withForm. وفي هنا يلزمنا تمرير دالة هكذا. فدالة، ودالة تُدعى render.

[03:22] ومن ثمّ سنمتلك في هنا إمكانية الوصول إلى group.

[03:26] صحيح؟ سأعيد ربما fragment لأنّني لا أريد أن أُغلّف حقولي بأيّ div.

[03:34] وفي هنا يمكننا استخدام هذا group الذي نحصل عليه هنا لتحديد حقولنا. فداخل group سنحصل على AppField وAppFormField وهلمّ جرا. فلترَ أنّه هذه المرة لن نحصل على نموذج هنا، لأنّ هذه الحقول ليست

[03:48] مربوطًا بنموذجٍ محدَّد، بل نحصل بدلًا من ذلك على هذا group، ويمكننا الآن الذهاب إلى نموذجنا ونسخ حقولنا، اه، حقول password وconfirmPassword. فلننسخ هذه، اه. فلنقطع هذه من هنا وسأذهب إلى مكوّن

[04:06] PasswordFields الخاص بي وألصق هذه فيه. غير أنّه لن نستخدم form الآن، بل سنستخدم group بدلًا منه هكذا.

[04:19] تمام.

[04:19] وبهذا يمكننا استخدام هذا المكوّن في أيّ نموذج، وهو ببساطة سيضيف هذه الحقول إلى أيّ نموذجٍ نستخدم فيه هذا المكوّن. ويمكننا كذلك — لنقل — أن يلزمنا داخل group إمكانية الوصول إلى أشياء مثل، ربما، أشياء

[04:33] متعلقةٍ بالنموذج. فربما يلزمنا، اه، الاشتراك في form.canSubmit مثلًا — ويمكننا فعل ذلك داخل group هذا، حيث سنمتلك الوصول إلى النموذج. فسيتوفر لدينا group.form — اه — وهذا النموذج سيكون

[04:46] أيّ نموذجٍ نستخدم فيه مكوّننا. فإذا احتجنا إلى الاشتراك في canSubmit مثلًا فسنستخدم في العادة الخُطّاف (hook) useStore. وكي نحصل على المخزن (store) لدينا نستخدم الآن group.form.store. ومن ثمّ

[04:59] سنحصل على state لدينا، ويمكننا أن نشترك في الأمور المتعلقة بالنموذج، اه، الذي نستخدم فيه هذا المكوّن.

[05:05] ولكن الآن لنحاول استخدام هذا المكوّن في نموذج التسجيل لدينا، وذلك بالذهاب إلى هنا ومن ثمّ في هنا فلنستورد مكوّن PasswordFields لدينا وسيلزمنا تمرير بعض الأشياء. فسيلزمنا تمريره إلى النموذج نفسه هكذا.

[05:22] كما سيلزمنا أيضًا تمرير خاصية (prop) fields له. فلنقل أنّه في نموذجنا — نموذج التسجيل — تذكّر أنّ لدينا قيمنا الافتراضية (defaultValues) وهلمّ جرا؛ هنا في هذا الملف المشترك، وفي مخططنا (schema)، تبدو حقول كلمة السر لدينا هكذا: لدينا password و

[05:41] confirmPassword. غير أنّه كان يمكن أن يكون الأمر كذلك هكذا. فربما كان لدينا passwordsData — اه — ثم متشعّبًا (nested) داخله كان لدينا password وconfirmPassword هكذا، صحيح. فسيلزمنا تحديد أين

[06:00] بالضبط تُخزَّن حقول كلمة السر لدينا في مخططنا (schema). فتذكّر أنّه في مكوّن PasswordFields لدينا نمتلك حقولين اثنين: password وconfirmPassword.

[06:09] ونحتاج إذًا إلى أن نُطابق، بطريقةٍ ما، هذه الحقول التي لدينا هنا في هذا المكوّن الفرعي — نُطابقها إلى الموضع الذي توجد به في مكوّننا الرئيسي. فإذا كانت موجودة — هكذا — داخل حقل passwordsData مثلًا فسيلزمنا أن نمرّر هنا

[06:27] في هذه الخاصية (prop) fields هذه — اه — passwordsData.

[06:32] وبفعلنا ذلك، فكلّ ما نكتبه في حقول كلمة السر لدينا سيوضع في passwordsData.password وفي passwordsData.confirmPassword. غير أنّه ليست هذه حالتنا في مخططنا (schema) لدينا. فلنزِل إذًا ما فعلناه هنا. في

[06:47] مخططنا (schema)، لا نمتلك password وconfirmPassword متشعّبين داخل مفتاح (key) آخر.

[06:50] غير أنّهما مفتاحان منفصلان في مخططنا (schema). فما يمكننا فعله إذًا هو أن نُطابق أيضًا أيَّ حقلٍ في مخططنا يقابل أيَّ حقلٍ في مكوّن PasswordFields لدينا. وفي حالتنا فهما الشيء ذاته. فـpassword يقابل

[07:04] password. وconfirmPassword يقابل confirmPassword. غير أنّه يمكن أن يكون لهما اسمان مختلفان، إذًا يمكننا أن نُطابقهما إلى أسماء مختلفة كذلك أيضًا. ولكن في حالتنا، اه، ليسا لهما اسمان مختلفان. فما يمكننا فعله إذًا أن نمرّر

[07:14] كائنًا (object) هنا هكذا. ومن ثمّ نحدّد أنّ حقل password لدينا يقابل password في مخططنا. فـpassword في مكوّن PasswordFields يقابل password في مخططنا. ثم confirmPassword في

[07:32] مكوّن PasswordFields لدينا يقابل confirmPassword في مخططنا هكذا. غير أنّه، اه، إذا كان هذا — إذا كان لهذه أسماء مختلفة، إذا كانت الحقول في PasswordFields ربما ذات أسماء مختلفة، فسنكتفي بتحديد ذلك في هذا التطابق (mapping) هنا.

[07:46] غير أنّه بهذا ينبغي أن يعمل هذا، واه، إذا كتبنا ربما شيئًا هنا — اه — فحسبًا للتأكّد من أنّ هذا يعمل، فلنفتح console لدينا كذلك أيضًا. تذكّر أنّنا نسجّل قيمنا عندما نُرسل نموذجنا. فلنُرسله إذًا.

[08:02] أوه، وعذرًا، ذلك أنّه لا بدّ لنا — اه — لا بدّ لنا من أن يكون لدينا نموذج صالح. فلنكتب، اه، أيَّ شيء هنا فحسب.

[08:10] وينبغي أن يكون هذا ستّة أحرف على الأقل.

[08:20] فلنوافق. والآن فلنُرسله. وسنحصل — اه — في هنا على password لدينا معبّأة، وأيضًا confirmPassword، مما يعني — اه — أنّ هذا يعمل على الوجه الصحيح وأنّنا نستخدم مكوّننا الفرعي هنا لغرض تعبئة بعض الحقول في

[08:34] نموذجنا الرئيسي. غير أنّه، الآن، يمكن ألّا يُستخدم هذا في هذا النموذج وحده فقط، بل يُستخدم في أيّ نموذج، ويمكنك فقط أن تُطابق الحقول هكذا، بحسب شكل نموذجك.

[08:45] ولكن أمرٌ أخير — اه — إذا لاحظت أنّ التحقق من الصحة (validation) لدينا قد اجتاز هنا — اه — فلنحدّث الصفحة ربما. لقد اجتاز التحقق من الصحة password وconfirmPassword لدينا. فسأكتب ربما كلمتي سر غير متطابقتين

[08:59] ونُرسل. وكما ترى، يعمل التحقق من الصحة. لماذا؟ لأنّه هنا في مخططنا الرئيسي لهذا النموذج كنّا نُجري هذا التحقق من الصحة. ففي هنا، في مخططنا في هذا المجلد المشترك، نُجري التحقق من

[09:14] كلمة السر هنا باستخدام refine. غير أنّه، بهذه الطريقة، سيلزمنا، كلّما استخدمنا هذه الحقول، أن نُجري التحقق من الصحة لكلِّ نموذج، لأنّ التحقق من الصحة لدينا يقيم عند مستوى النموذج، لا في هذه المجموعة من الحقول التي عرّفناها. فما

[09:30] يمكننا فعله إذًا أنّه يمكننا أن نزيل هذا من هنا. فسأفعل ربما — دعنا نعلّقه بالتعليقات (comment) فقط في الوقت الحالي. كذلك سأحذف حقول كلمة السر من هنا أيضًا. فلنحذف password ولنحذف confirmPassword، فلا نتوفّر على

[09:43] هذه الحقول في مخططنا (schema). صحيح؟ وما سأفعله إذًا أنّني سأعود إلى هذا المكوّن المشترك، اه، PasswordFields هنا. وبدلًا من تعريف النوع (type) هكذا، سأعرّف مخططًا (schema) هكذا يتضمّن — اه — سيلزمنا

[09:58] أيضًا استيراد z — اه، z — من Zod. فالآن في هنا عرّفتُ مخططًا يتضمّن حقل password الخاص بي وحقل confirmPassword، بكامل ما يلزمنا منه من تحققٍ من الصحة، وكذلك وضعتُ refine هنا بدلًا من ذلك، وهذا كي يمكننا أن

[10:14] نُعرّف تحققنا من صحة password وconfirmPassword مرةً واحدةً فقط في هذه المجموعة، لكنني عندئذٍ يلزمني فقط أن أُعدّل هذا — اه — والآن، بما أنّنا حذفنا نوع حقول كلمة السر لدينا، فلنستنبط (infer) نوعنا من مخططنا (schema) إذًا

[10:27] فلنستنتج — عذرًا، فلنستنتج — هكذا، ثم typeof، ثم مخططنا الذي سمّيناه passwordsSchema هكذا. والآن، ما سيلزمنا فعله — إذا لاحظت هنا أنّني أُصدّر

[10:41] المخطط، وما يلزمنا فعله في كلّ نموذجٍ يلزمنا فيه استخدام password وconfirmPassword — عندما نعرّف مخططَ هذا النموذج — فمثلًا هنا سيلزمنا استخدام — اه، عذرًا — في المجلد المشترك لدينا سيلزمنا استخدام مخططنا، و

[10:56] أشياء مخططنا: firstName وlastName وهلمّ جرا، إضافةً إلى password وconfirmPassword. غير أنّه بدلًا من أن نعرّف هذه هنا ونعرّف التحقق من صحة هذه هنا، يمكننا ببساطة أن نُمدّد المخططَ الذي عرّفناه في

[11:11] — اه — PasswordFields هنا. وللحصول على ذلك باستخدام Zod، يمكننا، بعد تعريف مخططنا، استدعاء `.and` ثم استيراد passwordsSchema هكذا.

[11:25] وبفعلنا ذلك، فكأنّنا نمتلك حقلي password وconfirmPassword لدينا وrefine كذلك أيضًا، ولكنّنا نعرّف هذه هنا بصورةٍ منفصلة كي نستطيع إعادة استخدامها في نماذج متعددة. وبهذا، إذا حدّثنا الصفحة كذلك أيضًا و

[11:41] كتبنا password، فينبغي أن يظل التحقق من الصحة لدينا يعمل كذلك أيضًا.

[11:48] فإذًا هذا كل ما في هذا الفيديو. أرجو أن تكون قد استمتعت به. احرص على ترك إعجاب، وترك تعليقٍ ربما، والاشتراك في القناة لمزيدٍ من المحتوى.