كيفية إنشاء React Hook مخصص ونشره كوحدة npm
مقدمة إلى React Hooks المخصصة: قوة إعادة الاستخدام
تُعد Hooks إضافة قيّمة لواجهة برمجة تطبيقات React API، حيث تتيح لنا تنظيم منطق الحالة (state) والمنطق البرمجي في المكونات الوظيفية (function components). ولكن كيف يمكننا بناء Hook مخصص ومشاركته مع مجتمع المطورين؟ في هذا المقال، سنتعمق في عملية إنشاء React Hook مخصص من البداية وحتى النشر على npm، مع التركيز على أفضل الممارسات لضمان محتوى عالي الجودة وقابل لإعادة الاستخدام.
ما هي Hooks؟
ببساطة، React Hooks هي دوال. عند تضمينها في مكونك أو داخل Hook آخر، فإنها تمكنك من الاستفادة من آليات React الداخلية وأجزاء من دورة حياة React باستخدام Hooks الأصلية مثل و useState. لن نخوض في تفاصيل عميقة حول useEffectHooks هنا، ولكن يمكنك الاطلاع على مقدمة سريعة مع مثال على ، بالإضافة إلى المقدمة الرسمية من فريق React.useState
لماذا تُعد Hooks المخصصة مفيدة؟
الميزة الرائعة في إنشاء Hooks مخصصة هي أنها تسمح لك بتجريد المنطق البرمجي لمكوناتك، مما يسهل إعادة استخدامه عبر مكونات متعددة في تطبيقك. هذا يعزز مبدأ "عدم تكرار نفسك" (DRY - Don't Repeat Yourself) ويجعل صيانة وتوسيع التعليمات البرمجية أسهل بكثير.

على سبيل المثال، إذا أردت إنشاء عداد بسيط تستخدم فيه حالة React لإدارة العدد الحالي، فبدلاً من تكرار في كل ملف مكون، يمكنك إنشاء هذا المنطق مرة واحدة في useStateHook مخصص مثل . هذا يجعل الصيانة والتوسيع وإصلاح الأخطاء أسهل بكثير إذا ظهرت.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
:camelCaseusePlaceCage:snake-caseuse-placecage
سيتم استخدام تنسيق لدالة camelCaseHook الفعلية، بينما سيتم استخدام اسم لاسم الحزمة (snake-casepackage name) وبعض المجلدات.
عند إنشاء الاسم، تذكر أن اسم الحزمة يجب أن يكون فريدًا. إذا كانت هناك حزمة بنفس الاسم موجودة بالفعل على npmjs.com، فلن تتمكن من استخدامها. إذا لم يكن لديك اسم بالفعل، فلا بأس! يمكنك فقط استخدام اسمك الخاص أو أي شيء يمكنك التفكير فيه، فالأمر لا يهم كثيرًا لأننا نحاول حقًا تعلم كيفية القيام بذلك.
على سبيل المثال، لو كنت أنا، فسأستخدم:
:camelCaseuseColbysCoolHook:snake-caseuse-colbyscoolhook
ولكن للتوضيح، لبقية مثالنا، سنلتزم بـ و usePlaceCage.use-placecage
الخطوة 1: إعداد مشروعك
على الرغم من أنه يمكنك إعداد مشروعك بالطريقة التي تفضلها، إلا أننا سنتبع عملية بناء Hook جديد من قالب قمت بإنشائه. الأمل هنا هو أن نتمكن من إزالة بعض الأجزاء المؤلمة من العملية والبدء فورًا في العمل بإنتاجية مع Hook المخصص الخاص بنا. لا تقلق، سأشرح ما يحدث على طول الطريق.
المتطلبات هنا هي و git، حيث تساعد في توفير أدوات تسهل إنشاء هذا القالب، مثل استخدام ميزة مساحات العمل (yarnworkspaces) للسماح لسكربتات بإدارة التعليمات البرمجية بسهولة من جذر المشروع. إذا كان أي من هذين الأمرين يمثل عائقًا، يمكنك محاولة تنزيل المستودع عبر رابط التنزيل وتحديثه حسب الحاجة.npm
استنساخ قالب الـ Hook من Git
للبدء، دعنا نستنسخ المستودع من GitHub. في الأمر أدناه، يجب أن تستبدل باسم use-my-custom-hookHook الخاص بك، مثل أو 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

سيوفر لك هذا بعض الأشياء للبدء:
- دليل
Hookسيتضمن الكود المصدري لـHookالخاص بنا. - سكربتات بناء (
Build scripts) تقوم بتجميعHookالخاص بنا باستخدام.Babel - صفحة مثال تستورد
Hookالخاص بنا وتنشئ صفحة تجريبية بسيطة باستخدامnext.js.
تشغيل سكربتات إعداد الـ Hook
بعد أن نستنسخ المستودع بنجاح، نريد تشغيل سكربتات الإعداد التي تقوم بتثبيت التبعيات وتحديث Hook إلى الاسم الذي نريده.
yarn install && yarn setup

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

مع كل هذا جاهزًا، يمكننا البدء! تابع مع التغييرات (commit)!
الخطوة 2: كتابة React Hook الجديد الخاص بك
في هذه المرحلة، يجب أن يكون لديك الآن Hook مخصص جديد يمكنك جعله يقوم بما تريده. ولكن بما أننا سنتبع إعادة بناء Hook الـ ، فلنبدأ من هناك.usePlaceCage
يقوم Hook الـ بشيء واحد بسيط من منظور عالي المستوى – يأخذ كائن تكوين (usePlaceCageconfiguration object) ويعيد عددًا من عناوين URL للصور التي يمكنك بعد ذلك استخدامها لتطبيقك. كتذكير، في أي وقت أذكر فيه أو usePlaceCage، يجب أن تستخدم اسم use-placecageHook الذي قمت بإعداده مسبقًا.
نبذة عن placecage.com
placecage.com هي خدمة صور نائبة تقوم بشيء واحد. تأخذ عنوان URL بتكوين بسيط وتعيد صورة… للممثل نيكولاس كيج.

من أبسط استخدام، تستخدم الخدمة نمط 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.jsURL للصورة، بالإضافة إلى بعض تعريفات الثوابت التي سنستخدمها في تلك الدالة.
أولاً، دعنا ننسخ ثوابتنا إلى الجزء العلوي من ملف الخاص بنا: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('/')}`);
}
شرح هذا الكود:
- نقوم بتعريف دالة
التي سنستخدمها لإنشاء عنوانgenerateCageURLلصورنا. - نأخذ كائن
كوسيطة، والذي يحدد تكوين عنوانsettingsURLلصورنا. سنستخدم نفس المعلمات التي رأيناها في عنوانURLلـplacecage.com. - نقوم بتفكيك (
destructure) تلك الإعدادات لجعلها متاحة لنا للاستخدام. - لدينا بعض القيم الافتراضية هنا لتسهيل الأمور. سيتم تعريف نوعنا الافتراضي بواسطة
جنبًا إلى جنب مع عرض وارتفاع افتراضيين وعدد النتائج التي نريد إرجاعها.DEFAULT_TYPE - نقوم بإنشاء مصفوفة
. سنستخدم هذا لإلحاق جميع كائنات التكوين المختلفة في عنوانconfigURLالخاص بنا وربطها أخيرًا بـلإنشاء عنوان/URLبشكل أساسي. - قبل دفع تكويننا إلى تلك المصفوفة، نتحقق مما إذا كانت وسيطة صالحة، باستخدام كائن
للتحقق منها. إذا كانت صالحة، ندفعها إلى مصفوفة التكوين الخاصة بنا.TYPES - ثم ندفع عرضنا وارتفاعنا.
- نقوم ببعض التحقق من النوع، إذا لم يكن لدينا رقم صالح كـ
، فإننا نلقي خطأ، وإلا فسنحصل على نتائج غير صحيحة.count - أخيرًا، نعيد مصفوفة جديدة بعدد النتائج المطلوبة، مع تعيينها إلى منشئ
URL، الذي يستخدمكعنوانPLACECAGE_HOSTURLالأساسي المحدد لدينا، ومع مصفوفة التكوين الخاصة بنا المربوطة بـ./
وإذا أردنا اختبار هذه الدالة، فسيبدو الأمر كالتالي:
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 ونعين القيمة إلى الثابت . إذا قمنا الآن بتسجيل (cageconsole log) تلك القيمة في أدوات المطور لدينا، يمكننا رؤيتها تعمل!
console.log('cage', cage);

ملاحظة: إذا حصلت على خطأ هنا بخصوص ، يمكنك التعليق عليه أو إزالته تحت قسم الأمثلة (messageExamples).
تحديث المثال بتكوين الـ Hook الجديد الخاص بنا
إذا قمت بالتمرير لأسفل إلى قسم الأمثلة (Examples)، ستلاحظ أن لدينا نفس الافتراضي كما هو مذكور أعلاه، لذا دعنا نحدثه مرة أخرى للتأكد من أن مثالنا دقيق.hookSettings
{ `const hookSettings = { type: 'gif', width: 500, height: 500, count: 2 } const cage = usePlaceCage(hookSettings);` }
ستلاحظ أيضًا أننا لم نعد نستخدم المتغير . إذا لم تقم بإزالته في الخطوة الأخيرة، يمكننا الآن استبداله تحت عنوان "الناتج" (messageOutput) بـ:
<p> { JSON.stringify(cage) } </p>
<p> { cage.map((img, i) => <img key={`img-${i}`} width={200} src={img} />) } </p>
نقوم هنا بشيئين:
- بدلاً من إظهار المتغير نفسه، نقوم بتغليفه بـ
حتى نتمكن من إظهار محتويات المصفوفة.JSON.stringify - نستخدم أيضًا دالة
للتكرار على عناوينmapURLلصورنا في الثابتوإنشاء عنصر صورة جديد لكل منها. يتيح لنا هذا معاينة الناتج بدلاً من مجرد رؤية القيم.cage
وبمجرد الحفظ وفتح متصفحك، يجب أن ترى الآن أمثلتك وناتجك المحدثين!

أشياء أخرى يمكنك القيام بها في تلك الصفحة
قبل الانتقال، يمكنك أيضًا تحديث بعض الأشياء الأخرى التي ستكون مهمة لصفحة 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.jsonHook الخاص بنا يدويًا باستخدام الأمر من جذر المشروع.yarn build
بالإضافة إلى تجميع Hook، سيقوم تشغيل الأمر أيضًا بتجميع صفحة المثال، مما يسمح لك بتحميلها أينما تريد. بعد تشغيل هذا الأمر، يجب أن ترى ناتجًا من ملفات yarn buildHTML ثابتة في دليل . إذا كنت تبحث عن توصية، فإن example/out يجعل من السهل ربط حساب NetlifyGitHub الخاص بك ونشر الموقع الثابت.

شاهد الموقع التجريبي المنشور على Netlify!
الخطوة 5: نشر React Hook الخاص بك على npm
أخيرًا، إذا كنت راضيًا عن Hook الخاص بك، فقد حان وقت النشر! يجعل هذا الجزء سهلاً حقًا. الشرط الوحيد هو أن يكون لديك حساب npm. باستخدام هذا الحساب، دعنا نسجل الدخول:npm
npm login
والذي سيطالبك ببيانات اعتماد تسجيل الدخول الخاصة بك. بعد ذلك، دعنا ننتقل إلى دليل Hook الخاص بنا، حيث يوجد تكوين الحزمة الخاص بنا تحت :use-placecage/package.json
cd use-placecage
ثم، يمكننا ببساطة النشر!
npm publish
تذكر أن كل اسم حزمة يجب أن يكون فريدًا. إذا استخدمت ، فهو محجوز بالفعل… من قبلي. 😅 ولكن إذا نجحت، يجب أن يقوم use-placecage ببناء npmHook الخاص بك وتحميله إلى سجل الحزم!

سيكون متاحًا بعد ذلك على بالنمط التالي:npm
https://www.npmjs.com/package/[package-name]
لذا بالنسبة لـ ، فهو متاح هنا: https://www.npmjs.com/package/use-placecageuse-placecage
لدينا الآن Hook مخصص! يا للروعة! 🎉 إذا تابعت، يجب أن تكون قد أنشأت الآن Hook مخصصًا ونشرته على . على الرغم من أن هذا كان مثالًا سخيفًا باستخدام npmplacecage.com، إلا أنه يعطينا فكرة جيدة عن كيفية إعداد هذا بسهولة. ستلاحظ أيضًا أن هذا المثال المحدد لم يكن أفضل حالة استخدام لـ Hooks، حيث كان بإمكاننا ببساطة استخدام دالة عادية. عادةً، سنرغب في استخدام Hooks المخصصة لتغليف الوظائف التي لا يمكن أن توجد إلا داخل مكون React، مثل . لمعرفة المزيد حول ذلك، يمكنك قراءة أحد مقالاتي الأخرى حول useStateHooks المخصصة. ومع ذلك، فقد أعطانا هذا أساسًا جيدًا للحديث عن إنشاء وتكوين Hook الجديد الخاص بنا!
المزيد من الموارد حول Hooks
- كيفية تفكيك أساسيات React Hooks (freecodecamp.org)
- مقدمة إلى Hooks (reactjs.org)
- مرجع Hooks API (reactjs.org)
الخلاصة التقنية
لقد استعرض هذا المقال ببراعة العملية الكاملة لإنشاء React Hook مخصص ونشره كوحدة . على الرغم من أن المثال المستخدم (npmusePlaceCage) كان بسيطًا، إلا أنه قدم إطارًا عمليًا لفهم المفاهيم الأساسية، بدءًا من التسمية الصحيحة للحزمة والدالة، مرورًا بإعداد بيئة التطوير باستخدام قوالب جاهزة، وصولاً إلى تجميع الكود ونشره. الأهمية الحقيقية لـ Hooks المخصصة تكمن في قدرتها على تجريد المنطق المعقد الذي يعتمد على Hooks الأصلية مثل و useState، مما يعزز قابلية إعادة الاستخدام ويقلل من تكرار التعليمات البرمجية عبر المكونات المختلفة. هذا النهج لا يساهم فقط في تنظيم التعليمات البرمجية بشكل أفضل، بل يسهل أيضًا صيانتها وتوسيعها، مما يجعلها أداة لا غنى عنها في تطوير تطبيقات useEffectReact الحديثة.