كيفية إنشاء React Hook مخصص ونشره كوحدة npm

دقائق القراءة: 13

مقدمة إلى React Hooks المخصصة: قوة إعادة الاستخدام

تُعد Hooks إضافة قيّمة لواجهة برمجة تطبيقات React API، حيث تتيح لنا تنظيم منطق الحالة (state) والمنطق البرمجي في المكونات الوظيفية (function components). ولكن كيف يمكننا بناء Hook مخصص ومشاركته مع مجتمع المطورين؟ في هذا المقال، سنتعمق في عملية إنشاء React Hook مخصص من البداية وحتى النشر على npm، مع التركيز على أفضل الممارسات لضمان محتوى عالي الجودة وقابل لإعادة الاستخدام.

ما هي Hooks؟

ببساطة، React Hooks هي دوال. عند تضمينها في مكونك أو داخل Hook آخر، فإنها تمكنك من الاستفادة من آليات React الداخلية وأجزاء من دورة حياة React باستخدام Hooks الأصلية مثل useState و useEffect. لن نخوض في تفاصيل عميقة حول Hooks هنا، ولكن يمكنك الاطلاع على مقدمة سريعة مع مثال على useState، بالإضافة إلى المقدمة الرسمية من فريق React.

لماذا تُعد Hooks المخصصة مفيدة؟

الميزة الرائعة في إنشاء Hooks مخصصة هي أنها تسمح لك بتجريد المنطق البرمجي لمكوناتك، مما يسهل إعادة استخدامه عبر مكونات متعددة في تطبيقك. هذا يعزز مبدأ "عدم تكرار نفسك" (DRY - Don't Repeat Yourself) ويجعل صيانة وتوسيع التعليمات البرمجية أسهل بكثير.

مخطط يوضح مفهوم الـ React Hook المخصص useCounter وكيفية إعادة استخدام المنطق.

على سبيل المثال، إذا أردت إنشاء عداد بسيط تستخدم فيه حالة React لإدارة العدد الحالي، فبدلاً من تكرار useState في كل ملف مكون، يمكنك إنشاء هذا المنطق مرة واحدة في Hook مخصص مثل useCounter. هذا يجعل الصيانة والتوسيع وإصلاح الأخطاء أسهل بكثير إذا ظهرت.

ماذا سنقوم بإنشائه؟

لأغراض هذا المقال، سنبقي الأمور بسيطة من خلال Hook أساسي. عادةً ما تستخدم Hook لأنك تحتاج إلى استخدام Hooks أصلية أخرى تتطلب أن تكون داخل مكونات React الوظيفية. سنلتزم ببعض المدخلات والمخرجات الأساسية للحفاظ على البساطة.

سنقوم بإعادة إنشاء Hook مخصص لـ Placecage، والذي يسمح لك بسهولة بإنشاء عناوين URL للصور التي يمكنك استخدامها كصور نائبة (placeholder images).

صورة متحركة للممثل نيكولاس كيج يعبر عن الحماس.

إذا لم تكن على دراية، فإن Placecage هي واجهة برمجة تطبيقات (API) تسمح لك بإنشاء صور للممثل نيكولاس كيج كصور نائبة لموقعك الإلكتروني. هل هو سخيف؟ نعم. هل هو ممتع؟ بالتأكيد! ولكن إذا لم تكن من محبي أعمال نيكولاس كيج، يمكنك بسهولة استبدال عنوان URL بـ Fill Murray الذي يستخدم صور بيل موراي، أو placeholder.com الذي ينشئ خلفيات بسيطة بلون واحد مع نص يوضح حجم الصورة.

الخطوة 0: تسمية الـ Hook الخاص بك

قبل أن نتعمق في التعليمات البرمجية الفعلية، هدفنا النهائي هو نشر هذا Hook. إذا لم يكن هذا هو هدفك، يمكنك تخطي هذه الخطوة، ولكن لأغراض النشر، سنحتاج إلى إنشاء اسم لـ Hook الخاص بنا. في حالتنا، سيكون اسم Hook هو usePlaceCage.

مع وضع ذلك في الاعتبار، لدينا صيغتان لاسمنا – واحدة بتنسيق camelCase والأخرى بتنسيق snake-case:

  • camelCase: usePlaceCage
  • snake-case: use-placecage

سيتم استخدام تنسيق camelCase لدالة Hook الفعلية، بينما سيتم استخدام اسم snake-case لاسم الحزمة (package name) وبعض المجلدات.

عند إنشاء الاسم، تذكر أن اسم الحزمة يجب أن يكون فريدًا. إذا كانت هناك حزمة بنفس الاسم موجودة بالفعل على npmjs.com، فلن تتمكن من استخدامها. إذا لم يكن لديك اسم بالفعل، فلا بأس! يمكنك فقط استخدام اسمك الخاص أو أي شيء يمكنك التفكير فيه، فالأمر لا يهم كثيرًا لأننا نحاول حقًا تعلم كيفية القيام بذلك.

على سبيل المثال، لو كنت أنا، فسأستخدم:

  • camelCase: useColbysCoolHook
  • snake-case: use-colbyscoolhook

ولكن للتوضيح، لبقية مثالنا، سنلتزم بـ usePlaceCage و use-placecage.

الخطوة 1: إعداد مشروعك

على الرغم من أنه يمكنك إعداد مشروعك بالطريقة التي تفضلها، إلا أننا سنتبع عملية بناء Hook جديد من قالب قمت بإنشائه. الأمل هنا هو أن نتمكن من إزالة بعض الأجزاء المؤلمة من العملية والبدء فورًا في العمل بإنتاجية مع Hook المخصص الخاص بنا. لا تقلق، سأشرح ما يحدث على طول الطريق.

المتطلبات هنا هي git و yarn، حيث تساعد في توفير أدوات تسهل إنشاء هذا القالب، مثل استخدام ميزة مساحات العمل (workspaces) للسماح لسكربتات npm بإدارة التعليمات البرمجية بسهولة من جذر المشروع. إذا كان أي من هذين الأمرين يمثل عائقًا، يمكنك محاولة تنزيل المستودع عبر رابط التنزيل وتحديثه حسب الحاجة.

استنساخ قالب الـ Hook من Git

للبدء، دعنا نستنسخ المستودع من GitHub. في الأمر أدناه، يجب أن تستبدل use-my-custom-hook باسم Hook الخاص بك، مثل use-cookies أو use-mooncake.

git clone https://github.com/colbyfayock/use-custom-hook use-my-custom-hook
cd use-my-custom-hook

بمجرد الاستنساخ والتنقل إلى هذا المجلد، يجب أن ترى الآن دليلين – دليل example ودليل use-custom-hook.

لقطة شاشة لعملية استنساخ قالب use-custom-hook من GitHub.

سيوفر لك هذا بعض الأشياء للبدء:

  • دليل Hook سيتضمن الكود المصدري لـ Hook الخاص بنا.
  • سكربتات بناء (Build scripts) تقوم بتجميع Hook الخاص بنا باستخدام Babel.
  • صفحة مثال تستورد Hook الخاص بنا وتنشئ صفحة تجريبية بسيطة باستخدام next.js.

تشغيل سكربتات إعداد الـ Hook

بعد أن نستنسخ المستودع بنجاح، نريد تشغيل سكربتات الإعداد التي تقوم بتثبيت التبعيات وتحديث Hook إلى الاسم الذي نريده.

yarn install && yarn setup

لقطة شاشة لعملية إعداد قالب الـ React Hook الجديد باستخدام yarn setup.

عند تشغيل سكربت الإعداد، سيقوم ببعض الأشياء:

  • سيطلب منك اسمك – يستخدم هذا لتحديث ملف LICENSE واسم مؤلف الحزمة.
  • سيطلب منك اسم Hook الخاص بك في صيغتين – camelCase و snake-case – سيتم استخدام هذا لتحديث اسم Hook في جميع أنحاء القالب ونقل الملفات بهذا الاسم إلى الموقع الصحيح.
  • سيقوم بإعادة تعيين git – سيقوم أولاً بإزالة مجلد .git المحلي، الذي يحتوي على سجل القالب الخاص بي، وإعادة تهيئة git بتثبيت جديد لبدء سجلك الجديد.
  • أخيرًا، سيقوم بإزالة دليل سكربت الإعداد وإزالة تبعيات الحزمة التي كانت تستخدمها تلك السكربتات فقط.

بدء خادم التطوير

بمجرد انتهاء سكربتات الإعداد من التشغيل، ستحتاج إلى تشغيل:

yarn develop

يقوم هذا بتشغيل عملية مراقبة على الكود المصدري لـ Hook، وبناء Hook محليًا في كل مرة يتم فيها تغيير ملف مصدري، وتشغيل خادم تطبيق المثال، حيث يمكنك اختبار Hook وإجراء تغييرات على صفحات المثال.

لقطة شاشة لخادم التطوير الخاص بالـ React Hook المخصص قيد التشغيل.

مع كل هذا جاهزًا، يمكننا البدء! تابع مع التغييرات (commit)!

الخطوة 2: كتابة React Hook الجديد الخاص بك

في هذه المرحلة، يجب أن يكون لديك الآن Hook مخصص جديد يمكنك جعله يقوم بما تريده. ولكن بما أننا سنتبع إعادة بناء Hook الـ usePlaceCage، فلنبدأ من هناك.

يقوم Hook الـ usePlaceCage بشيء واحد بسيط من منظور عالي المستوى – يأخذ كائن تكوين (configuration object) ويعيد عددًا من عناوين URL للصور التي يمكنك بعد ذلك استخدامها لتطبيقك. كتذكير، في أي وقت أذكر فيه usePlaceCage أو use-placecage، يجب أن تستخدم اسم Hook الذي قمت بإعداده مسبقًا.

نبذة عن placecage.com

placecage.com هي خدمة صور نائبة تقوم بشيء واحد. تأخذ عنوان URL بتكوين بسيط وتعيد صورة… للممثل نيكولاس كيج.

لقطة شاشة لموقع placecage.com الذي يوفر صوراً لنيكولاس كيج كصور نائبة.

من أبسط استخدام، تستخدم الخدمة نمط URL كالتالي:

https://www.placecage.com/200/300

سيعيد هذا صورة بعرض 200 وارتفاع 300. اختياريًا، يمكنك تمرير معلمة URL إضافية تحدد نوع الصورة:

https://www.placecage.com/gif/200/300

في هذه الحالة بالذات، نوعنا هو gif، لذلك سنتلقى صورة متحركة. الأنواع المختلفة المتاحة للاستخدام هي:

  • لا شيء: calm (هادئ)
  • g: gray (رمادي)
  • c: crazy (مجنون)
  • gif: gif (صورة متحركة)

سنستخدم هذا لتحديد كيفية إعداد التكوين لـ Hook الخاص بنا.

تعريف دالة المولد الأساسية

للبدء، سنقوم بنسخ دالة في الجزء السفلي من ملف use-placecage/src/usePlaceCage.js الخاص بنا، والتي تسمح لنا بإنشاء عنوان URL للصورة، بالإضافة إلى بعض تعريفات الثوابت التي سنستخدمها في تلك الدالة.

أولاً، دعنا ننسخ ثوابتنا إلى الجزء العلوي من ملف usePlaceCage.js الخاص بنا:

const PLACECAGE_HOST = 'https://www.placecage.com/';
const TYPES = {
  calm: null,
  gray: 'g',
  crazy: 'c',
  gif: 'gif'
};
const DEFAULT_TYPE = 'calm';
const ERROR_BASE = 'Failed to place Nick';

هنا نقوم بما يلي:

  • تعريف مضيف (host)، وهو عنوان URL الأساسي لخدمة الصور لدينا.
  • تعريف الأنواع المتاحة (TYPES)، والتي سنستخدمها في واجهة برمجة تطبيقات التكوين. قمنا بتعيين calm إلى null، لأنه القيمة الافتراضية التي تحصل عليها بعدم تضمينه على الإطلاق.
  • سيكون نوعنا الافتراضي هو calm.
  • وقمنا بتعيين أساس خطأ (ERROR_BASE) وهي رسالة متسقة عند إلقاء خطأ.

ثم لدالتنا، دعنا ننسخ هذا في الجزء السفلي من ملف usePlaceCage.js الخاص بنا:

function generateCage(settings) {
  const { type = DEFAULT_TYPE, width = 200, height = 200, count = 1 } = settings;

  const config = [];

  if ( type !== DEFAULT_TYPE && TYPES[type] ) {
    config.push(TYPES[type]);
  }

  config.push(width, height);

  if ( isNaN(count) ) {
    throw new Error(`${ERROR_BASE}: Invalid count ${count}`);
  }

  return [...new Array(count)].map(() => `${PLACECAGE_HOST}${config.join('/')}`);
}

شرح هذا الكود:

  • نقوم بتعريف دالة generateCage التي سنستخدمها لإنشاء عنوان URL لصورنا.
  • نأخذ كائن settings كوسيطة، والذي يحدد تكوين عنوان URL لصورنا. سنستخدم نفس المعلمات التي رأيناها في عنوان URL لـ placecage.com.
  • نقوم بتفكيك (destructure) تلك الإعدادات لجعلها متاحة لنا للاستخدام.
  • لدينا بعض القيم الافتراضية هنا لتسهيل الأمور. سيتم تعريف نوعنا الافتراضي بواسطة DEFAULT_TYPE جنبًا إلى جنب مع عرض وارتفاع افتراضيين وعدد النتائج التي نريد إرجاعها.
  • نقوم بإنشاء مصفوفة config. سنستخدم هذا لإلحاق جميع كائنات التكوين المختلفة في عنوان URL الخاص بنا وربطها أخيرًا بـ / لإنشاء عنوان URL بشكل أساسي.
  • قبل دفع تكويننا إلى تلك المصفوفة، نتحقق مما إذا كانت وسيطة صالحة، باستخدام كائن TYPES للتحقق منها. إذا كانت صالحة، ندفعها إلى مصفوفة التكوين الخاصة بنا.
  • ثم ندفع عرضنا وارتفاعنا.
  • نقوم ببعض التحقق من النوع، إذا لم يكن لدينا رقم صالح كـ count، فإننا نلقي خطأ، وإلا فسنحصل على نتائج غير صحيحة.
  • أخيرًا، نعيد مصفوفة جديدة بعدد النتائج المطلوبة، مع تعيينها إلى منشئ URL، الذي يستخدم PLACECAGE_HOST كعنوان URL الأساسي المحدد لدينا، ومع مصفوفة التكوين الخاصة بنا المربوطة بـ /.

وإذا أردنا اختبار هذه الدالة، فسيبدو الأمر كالتالي:

const cage = generateCage({
  type: 'gif',
  width: 500,
  height: 500,
  count: 2
});
console.log(cage);
// ['https://www.placecage.com/gif/500/500', 'https://www.placecage.com/gif/500/500']

استخدام دالتنا في الـ Hook

الآن بعد أن أصبح لدينا دالة المولد الخاصة بنا، دعنا نستخدمها بالفعل في Hook الخاص بنا! داخل دالة usePlaceCage في ملف use-placecage/src/usePlaceCage.js، يمكننا إضافة:

export default function usePlaceCage(settings = {}) {
  return generateCage(settings);
}

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

const cage = usePlaceCage({
  type: 'gif',
  width: 500,
  height: 500,
  count: 2
});
console.log(cage);
// ['https://www.placecage.com/gif/500/500', 'https://www.placecage.com/gif/500/500']

في هذه المرحلة، يقوم بنفس الشيء! لذلك لدينا الآن Hook الخاص بنا، وهو يعمل كدالة لإنشاء عناوين URL للصور لخدمة placecage.com. كيف يمكننا استخدامه بالفعل؟ تابع مع التغييرات (commit)!

الخطوة 3: استخدام React Hook الخاص بك في مثال

الخبر السار حول القالب الخاص بنا هو أنه يتضمن بالفعل تطبيق مثال يمكننا تحديثه للاستفادة بسهولة من Hook الخاص بنا لاختباره وتوفير وثائق لأولئك الذين يرغبون في استخدامه.

إعداد الـ Hook

للبدء، دعنا نفتح ملف example/pages/index.js الخاص بنا. داخل هذا الملف سترى ما يلي:

const hookSettings = {
  message: 'Hello, custom hook!'
}
const { message } = usePlaceCage(hookSettings);

هذا المقتطف هو ما تم استخدامه افتراضيًا في القالب لمجرد إثبات المفهوم، لذلك دعنا نحدثه. سنستخدم نفس التكوين تمامًا كما فعلنا في الخطوة 2:

const hookSettings = {
  type: 'gif',
  width: 500,
  height: 500,
  count: 2
}
const cage = usePlaceCage(hookSettings);

مرة أخرى، نقوم بإعداد كائن الإعدادات الخاص بنا مع تكوين Hook الخاص بنا ونستدعي Hook ونعين القيمة إلى الثابت cage. إذا قمنا الآن بتسجيل (console log) تلك القيمة في أدوات المطور لدينا، يمكننا رؤيتها تعمل!

console.log('cage', cage);

لقطة شاشة لناتج console.log يظهر مصفوفة عناوين URL لصور Placecage.

ملاحظة: إذا حصلت على خطأ هنا بخصوص message، يمكنك التعليق عليه أو إزالته تحت قسم الأمثلة (Examples).

تحديث المثال بتكوين الـ Hook الجديد الخاص بنا

إذا قمت بالتمرير لأسفل إلى قسم الأمثلة (Examples)، ستلاحظ أن لدينا نفس hookSettings الافتراضي كما هو مذكور أعلاه، لذا دعنا نحدثه مرة أخرى للتأكد من أن مثالنا دقيق.

{ `const hookSettings = { type: 'gif', width: 500, height: 500, count: 2 } const cage = usePlaceCage(hookSettings);` }

ستلاحظ أيضًا أننا لم نعد نستخدم المتغير message. إذا لم تقم بإزالته في الخطوة الأخيرة، يمكننا الآن استبداله تحت عنوان "الناتج" (Output) بـ:

<p> { JSON.stringify(cage) } </p>
<p> { cage.map((img, i) => <img key={`img-${i}`} width={200} src={img} />) } </p>

نقوم هنا بشيئين:

  • بدلاً من إظهار المتغير نفسه، نقوم بتغليفه بـ JSON.stringify حتى نتمكن من إظهار محتويات المصفوفة.
  • نستخدم أيضًا دالة map للتكرار على عناوين URL لصورنا في الثابت cage وإنشاء عنصر صورة جديد لكل منها. يتيح لنا هذا معاينة الناتج بدلاً من مجرد رؤية القيم.

وبمجرد الحفظ وفتح متصفحك، يجب أن ترى الآن أمثلتك وناتجك المحدثين!

لقطة شاشة لصفحة المثال التي تعرض الـ React Hook المخصص usePlaceCage قيد العمل.

أشياء أخرى يمكنك القيام بها في تلك الصفحة

قبل الانتقال، يمكنك أيضًا تحديث بعض الأشياء الأخرى التي ستكون مهمة لصفحة Hooks الخاصة بك:

  • تحديث قسم "كيفية الاستخدام" (How to use) بالتعليمات.
  • إضافة أمثلة إضافية لتسهيل معرفة ما يجب فعله للأشخاص.

يتم سحب بعض الأشياء تلقائيًا من ملف use-placecage/package.json. يمكنك إما تحديثها هناك لتسهيل الصيانة أو يمكنك استبدالها في صفحة المثال:

  • name: يستخدم في وسم <h1> للصفحة.
  • description: يستخدم في الوصف تحت وسم <h1>.
  • repository.url: يستخدم لتضمين رابط إلى المستودع.
  • author: يستخدم name و url لتضمين رابط في الجزء السفلي من الصفحة.

تابع مع التغييرات (commit)!

الخطوة 4: تجميع React Hook والمثال الخاص بك

الطريقة التي يمكننا بها جعل Hook الخاص بنا يعمل بسهولة كوحدة npm هي تجميعها ليستخدمها الآخرون. نحن نستخدم Babel للقيام بذلك. على الرغم من أن عملية النشر تقوم بذلك تلقائيًا لنا باستخدام سكربت prepublishOnly في ملف use-placecage/package.json، يمكننا تجميع Hook الخاص بنا يدويًا باستخدام الأمر yarn build من جذر المشروع.

بالإضافة إلى تجميع Hook، سيقوم تشغيل الأمر yarn build أيضًا بتجميع صفحة المثال، مما يسمح لك بتحميلها أينما تريد. بعد تشغيل هذا الأمر، يجب أن ترى ناتجًا من ملفات HTML ثابتة في دليل example/out. إذا كنت تبحث عن توصية، فإن Netlify يجعل من السهل ربط حساب GitHub الخاص بك ونشر الموقع الثابت.

لقطة شاشة لإعداد النشر في Netlify.

شاهد الموقع التجريبي المنشور على Netlify!

الخطوة 5: نشر React Hook الخاص بك على npm

أخيرًا، إذا كنت راضيًا عن Hook الخاص بك، فقد حان وقت النشر! npm يجعل هذا الجزء سهلاً حقًا. الشرط الوحيد هو أن يكون لديك حساب npm. باستخدام هذا الحساب، دعنا نسجل الدخول:

npm login

والذي سيطالبك ببيانات اعتماد تسجيل الدخول الخاصة بك. بعد ذلك، دعنا ننتقل إلى دليل Hook الخاص بنا، حيث يوجد تكوين الحزمة الخاص بنا تحت use-placecage/package.json:

cd use-placecage

ثم، يمكننا ببساطة النشر!

npm publish

تذكر أن كل اسم حزمة يجب أن يكون فريدًا. إذا استخدمت use-placecage، فهو محجوز بالفعل… من قبلي. 😅 ولكن إذا نجحت، يجب أن يقوم npm ببناء Hook الخاص بك وتحميله إلى سجل الحزم!

لقطة شاشة لعملية نشر حزمة npm بنجاح.

سيكون متاحًا بعد ذلك على npm بالنمط التالي:

https://www.npmjs.com/package/[package-name]

لذا بالنسبة لـ use-placecage، فهو متاح هنا: https://www.npmjs.com/package/use-placecage

لدينا الآن Hook مخصص! يا للروعة! 🎉 إذا تابعت، يجب أن تكون قد أنشأت الآن Hook مخصصًا ونشرته على npm. على الرغم من أن هذا كان مثالًا سخيفًا باستخدام placecage.com، إلا أنه يعطينا فكرة جيدة عن كيفية إعداد هذا بسهولة. ستلاحظ أيضًا أن هذا المثال المحدد لم يكن أفضل حالة استخدام لـ Hooks، حيث كان بإمكاننا ببساطة استخدام دالة عادية. عادةً، سنرغب في استخدام Hooks المخصصة لتغليف الوظائف التي لا يمكن أن توجد إلا داخل مكون React، مثل useState. لمعرفة المزيد حول ذلك، يمكنك قراءة أحد مقالاتي الأخرى حول Hooks المخصصة. ومع ذلك، فقد أعطانا هذا أساسًا جيدًا للحديث عن إنشاء وتكوين Hook الجديد الخاص بنا!

المزيد من الموارد حول Hooks

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

لقد استعرض هذا المقال ببراعة العملية الكاملة لإنشاء React Hook مخصص ونشره كوحدة npm. على الرغم من أن المثال المستخدم (usePlaceCage) كان بسيطًا، إلا أنه قدم إطارًا عمليًا لفهم المفاهيم الأساسية، بدءًا من التسمية الصحيحة للحزمة والدالة، مرورًا بإعداد بيئة التطوير باستخدام قوالب جاهزة، وصولاً إلى تجميع الكود ونشره. الأهمية الحقيقية لـ Hooks المخصصة تكمن في قدرتها على تجريد المنطق المعقد الذي يعتمد على Hooks الأصلية مثل useState و useEffect، مما يعزز قابلية إعادة الاستخدام ويقلل من تكرار التعليمات البرمجية عبر المكونات المختلفة. هذا النهج لا يساهم فقط في تنظيم التعليمات البرمجية بشكل أفضل، بل يسهل أيضًا صيانتها وتوسيعها، مما يجعلها أداة لا غنى عنها في تطوير تطبيقات React الحديثة.

اترك تعليقاً

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