الفرق بين HOCs و Custom Hooks في React: رحلة تطور مشاركة اللوجيك
أهلاً بيك يا بشمهندس في مقال جديد من سلسلة محترفي تطوير الويب (Web Development). لو انت شغال بـ ريأكت (React) بقالك فترة، فغالباً جيت في وقت وقعدت تبص للكود بتاعك ولقيت نفسك بتكرر نفس اللوجيك في كذا كومبوننت (Component). ساعتها سألت نفسك السؤال الشهير: "هو أنا كل ما أحتاج أجيب داتا من الـ API أو أسمع للـ Window Resize هكتب نفس الكود تاني؟!". هنا بتبدأ رحلة البحث عن إعادة استخدام اللوجيك (Logic Reuse).
زمان، الدنيا ماكنتش ورديّة كده، وكان فيه حرب ومشاكل لحد ما وصلنا للثورة اللي غيرت شكل ريأكت كله. في المقال ده، هنتكلم بالتفصيل عن تطور مشاركة اللوجيك، وهنعرف إيه هي المكونات عالية الرتبة أو الـ Higher-Order Components (HOCs)، وليه الـ هوكس المخصصة أو الـ Custom Hooks انتصرت عليها وبقت هي المعيار الأساسي دلوقتي. يلا بينا نفكك الموضوع سوا!
Table of contents [Show]
إيه هو وجع المبرمج القديم؟ وليه احتجنا مشاركة اللوجيك؟
في بداية أيام ريأكت، لما كان الكود بيعتمد على الـ Class Components، قابلتنا مشكلة كبيرة جداً: إزاي نشارك لوجيك معين (زي الـ State Management أو الـ Side Effects) بين أكتر من كومبوننت من غير ما نكرر الكود (DRY Principle - Don't Repeat Yourself)؟
المطورين فكروا في حلول عبقرية وقتها، وكان أبرزها الـ Render Props والـ Higher-Order Components (HOCs). الحلول دي كانت شغالة وتمام، بس للأسف كان ليها عيوب قاتلة بتظهر كل ما المشروع بيكبر، وأبرزها تعقيد الشجرة البرمجية وتداخل الـ Wrappers بشكل يدوخ.
ما هي الـ Higher-Order Components (HOCs) وكيف كانت تعمل؟
الـ Higher-Order Component أو الـ (HOC) مش كومبوننت حقيقي في حد ذاته، دي عبارة عن "دالة" (Function) بتأخذ كومبوننت قديم، وتعمل عليه تعديلات، وترجع لنا كومبوننت جديد مطور وفيه مميزات زيادة.
ببساطة شديدة، فكر فيها كأنها مصنع: بتدخل له منتج خام (Component)، بيحط عليه كيس وتغليف (Props إضافية أو Logic)، ويطلع لك منتج نهائي جاهز للبيع.
تعال نشوف مثال عملي سريع لشكل الـ HOC زمان:
// دي HOC بتاخد Component وتضيف عليه لوجيك معين
function withUser(WrappedComponent) {
return function WithUserComponent(props) {
const [user, setUser] = React.useState({ name: 'أحمد' });
return <WrappedComponent user={user} {...props} />;
};
}
// الاستخدام
function Profile({ user }) {
return <div>أهلاً بيك يا {user.name}</div>;
}
export default withUser(Profile);
الموضوع شكله لطيف وسهل في البداية، صح؟ بس استنى عليا شوية لما تشوف المشاكل اللي بتطلع لما نبدأ نعمل Composing لأكتر من HOC مع بعض (يعني تحط HOC جوه HOC جوه HOC).
عيوب الـ HOCs التي أدت لسقوطها
رغم إن الـ HOCs حلت مشاكل وقتها، إلا إنها جابت معاها مشاكل تانية تقيلة على قلب أي مبرمج:
- تلوث الـ Props (Props Pollution): الـ HOC بتضطر تمرير الـ Props للكومبوننت الأصلي، وده ممكن يعمل تعارض (Conflicts) في أسماء الـ Props لو عندك كذا HOC بيستخدموا نفس الاسم.
- شجرة الكومبوننت المرعبة (Wrapper Hell): لما تفتح الـ React DevTools وتلاقي شجرة الكومبوننت مليانة Wrappers و Providers و HOCs وراء بعض بالشكل ده:
<WithAuth><WithTheme><WithRouter><MyComponent /></WithRouter></WithTheme><WithAuth>، هتفقد صوابك وأنت بتعمل Debugging! - صعوبة تتبع مصدر الداتا: بتبقى مش عارف الـ Prop الفلاني جاي من أنهي HOC بالظبط، هل هو جاي من الـ Parent ولا من الـ Wrapper؟
ثورة ريأكت: ظهور الـ Custom Hooks
في سنة 2018، ومع إصدار React 16.8، حصلت ثورة حقيقية في عالم الـ Web Development بظهور الـ Hooks. ريأكت فتحت الباب للمطورين عشان يعملوا الـ Custom Hooks الخاصة بهم.
الـ Custom Hook هي ببساطة دالة عادية جداً بتبدأ بكلمة use، وتقدر تستخدم جواها أي Built-in Hook زي useState أو useEffect. الجمال هنا إنك بتقدر تفصل اللوجيك عن الـ UI تماماً!
تعال نشوف نفس مثال الـ User اللي فات، بس بالطريقة الحديثة باستخدام Custom Hook:
// دي Custom Hook مسؤولة عن جلب أو إدارة الـ User
function useUser() {
const [user, setUser] = React.useState({ name: 'أحمد' });
// ممكن تحط هنا منطق جلب داتا من API أو غيره
return { user, setUser };
}
// الاستخدام في الكومبوننت بقى أنظف بكتير
function Profile() {
const { user } = useUser();
return <div>أهلاً بيك يا {user.name}</div>;
}
شايف الكود بقات أنظف وأبسط إزاي؟ مفيش Wrapper Hell، ومفيش Props بتمررها بالغصب، ومفيش تعقيد في الشجرة!
لماذا انتصرت الـ Custom Hooks على الـ HOCs؟ المقارنة الحاسمة
لما نقارن الاتنين ببعض، هنفهم ليه مجتمع ريأكت كله ساب الـ HOCs وراح للـ Hooks:
- نظافة الكود (Clean Code): الـ Custom Hooks بتخلي الكومبوننت الرئيسي قصير ونظيف ومسؤول فقط عن عرض الـ UI، بينما اللوجيك كله مكود جوه الهوك بعيد عن العين.
- غياب الـ Wrappers: زي ما وضحنا، مفيش حاجة اسمها تداخل Wrappers، الكومبوننت بينزل في شجرة العناصر بشكل مباشر وسلس.
- سهولة الاستخدام (Flexibility): تقدر تستخدم أكتر من Custom Hook جوه نفس الكومبوننت من غير أي قلق من تداخل أسماء المتغيرات، لأن كل هوك ليه الـ Scope الخاص بيه.
- التوافقية والـ Testing:: اختبار (Testing) الـ Custom Hooks أسهل بكتير جداً مقارنة باختبار الـ HOCs اللي كانت بتتطلب تركيب ومحاكاة كاملة للـ Component Tree.
خاتمة ونصيحة أخيرة لتطوير مهاراتك
يا بشمهندس، التطور التكنولوجي في عالم الـ Frontend سريع جداً، واللي كان "موضة" وصح امبارح، ممكن يكون قديم وبطيء النهاردة. فهمك لـ ليه الـ HOCs طلعت وإيه عيوبها، بيخليك تفهم "فلسفة" ريأكت صح، ومش بس حافظ كود بتكتبه وخلاص.
نصيحتي ليك: ابعد تماماً عن استخدام الـ HOCs في مشاريعك الجديدة إلا لو مجبر جداً (مثلاً لو بتتعامل مع مكتبة قديمة لسه بتدعمها)، وركز كل تركيزك على إتقان الـ Custom Hooks، واعرف إزاي تقسم اللوجيك بتاعك بطريقة احترافية ونظيفة. بالتوفيق ليك، ودايماً اكتب كود يفرح اللي هيجي بعدك!