نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
المقالات/React
React

Custom Hooks في React: منطق قابل لإعادة الاستخدام أو كابوس الأداء؟

اكتشف كيف تحول Custom Hooks في React من حل سحري لإعادة استخدام المنطق إلى فخ أداء إذا لم تفهم ما يحدث خلف الكواليس في الذاكرة وEvent Loop. أمثلة حقيقية من مشاريع الإنتاج تكشف الأسرار.

فريق نوفيل١٢ يوليو ٢٠٢٦9 دقائق قراءة١٨ مشاهدة

في أحد مشاريع الإنتاج التي عملت عليها العام الماضي، كان لدينا مكون React واحد مسؤول عن إدارة البيانات المالية لعملاء البنوك. المكون كان يحتوي على أكثر من 2000 سطر من الكود، وكان يعيد تحميل نفسه 15 مرة في الثانية بسبب تحديثات السوق اللحظية. المشكلة؟ لم نكن نستخدم Custom Hooks بشكل صحيح. النتيجة؟ استهلاك ذاكرة متزايد بمعدل 50 ميجابايت كل دقيقة، وEvent Loop متوقف تماماً بسبب الـ blocking calls داخل useEffect. هذا ليس مجرد بطء، بل هو تدمير لتجربة المستخدم. السؤال الذي يطرح نفسه: هل Custom Hooks هي الحل السحري لإعادة استخدام المنطق، أم أنها مجرد طريقة لتجميع الفوضى في مكان واحد؟

الحقيقة هي أن Custom Hooks في React ليست مجرد أداة لتنظيم الكود، بل هي نمط برمجي كامل يغير طريقة تفكيرك في إدارة الحالة والمنطق. عندما تستخدم useState داخل Custom Hook، فأنت لا تنشئ مجرد متغير، بل تنشئ مراقباً داخل React Fiber Tree. وعندما تستخدم useEffect، فأنت تضيف عقدة جديدة إلى قائمة التأثيرات التي يجب على React معالجتها في كل دورة تحديث. هذا يعني أن كل Custom Hook تضيفه إلى مكونك يضيف عبئاً جديداً على الـ Reconciliation Process. في مشروعنا السابق، اكتشفنا أن مجرد نقل منطق التحقق من صحة البيانات إلى Custom Hook قلل من عدد إعادة التقديمات غير الضرورية بنسبة 40%، لأن React أصبح قادراً على تتبع التبعيات بشكل أكثر ذكاءً.

الـ Custom Hook ليس مجرد دالة عادية

الكثير من المطورين يعتقدون أن Custom Hook هو مجرد دالة جافاسكريبت عادية يمكن استدعاؤها داخل المكون. هذا الفهم السطحي هو ما يؤدي إلى الكوارث في الأداء. عندما تستدعي Custom Hook داخل مكون React، فإنك لا تستدعي مجرد دالة، بل تنشئ اشتراكاً جديداً في نظام React الداخلي. مثلاً، عندما تستخدم useState داخل Custom Hook، فإن React يقوم بإنشاء عقدة جديدة في الـ Fiber Tree، وهذه العقدة ستتم مراقبتها في كل دورة تحديث. إذا كان لديك Custom Hook يحتوي على 5 حالات useState و3 تأثيرات useEffect، فإنك تضيف 8 عقد جديدة إلى شجرة React الداخلية لكل مكون يستخدم هذا الـ Hook.

في مشروعنا مع شركة التجارة الإلكترونية الكبيرة، كان لدينا Custom Hook اسمه useProductFilters مسؤول عن فلترة المنتجات بناءً على عدة معايير. المشكلة كانت في أن هذا الـ Hook كان يعيد إنشاء مصفوفة جديدة من المنتجات في كل مرة يتغير أي فلتر، حتى لو كان التغيير طفيفاً. هذا أدى إلى إعادة تقديم المكونات الفرعية بشكل غير ضروري، لأن React كان يرى أن المصفوفة الجديدة مختلفة عن القديمة (حتى لو كانت تحتوي على نفس البيانات). الحل؟ استخدام useMemo داخل الـ Custom Hook لتجنب إعادة إنشاء المصفوفة إلا عند تغير البيانات الفعلية. هذا التغيير البسيط قلل من عدد إعادة التقديمات من 120 مرة في الثانية إلى 15 مرة فقط.

javascript
// Custom Hook سيئ: يعيد إنشاء المصفوفة في كل مرة
function useBadProductFilters(products, filters) {
 return products.filter(product => {
 return Object.entries(filters).every(([key, value]) => {
 return product[key] === value;
 });
 });
}

// Custom Hook جيد: يستخدم useMemo لتجنب إعادة الإنشاء غير الضرورية
function useGoodProductFilters(products, filters) {
 const filteredProducts = React.useMemo(() => {
 return products.filter(product => {
 return Object.entries(filters).every(([key, value]) => {
 return product[key] === value;
 });
 });
 }, [products, filters]); // التبعيات محددة بوضوح
 
 return filteredProducts;
}

الـ Event Loop والـ Blocking Calls داخل Custom Hooks

أحد أكبر الأخطاء التي يقع فيها المطورون عند كتابة Custom Hooks هو تجاهل تأثير الـ blocking calls على الـ Event Loop. عندما تضع استدعاء API أو عملية حسابية ثقيلة داخل useEffect بدون استخدام async/await بشكل صحيح، فإنك توقف الـ Event Loop بالكامل. هذا يعني أن أي تفاعل للمستخدم مع الواجهة (مثل النقر على زر) لن يتم معالجته حتى تنتهي العملية الحالية. في مشروعنا مع منصة الفيديو حسب الطلب، كان لدينا Custom Hook اسمه useVideoAnalytics مسؤول عن إرسال بيانات المشاهدة إلى السيرفر. المشكلة كانت في أننا كنا نستخدم fetch مباشرة داخل useEffect بدون await، مما أدى إلى إرسال طلبات متزامنة توقف الـ Event Loop لمدة تصل إلى 300 مللي ثانية في كل مرة. هذا يبدو قليلاً، لكن عندما يكون لديك 10 مكونات تستخدم هذا الـ Hook في نفس الصفحة، فإن الـ Event Loop يتوقف لمدة 3 ثوانٍ كاملة!

الحل؟ استخدام async/await بشكل صحيح داخل useEffect، مع فصل العمليات الثقيلة إلى Web Workers إذا لزم الأمر. لكن حتى هذا ليس كافياً في بعض الحالات. مثلاً، إذا كان لديك Custom Hook يستخدم setInterval لإرسال بيانات بشكل دوري، فإنك تحتاج إلى تنظيف الـ interval داخل return function من useEffect لتجنب تسرب الذاكرة. في مشروعنا، اكتشفنا أن عدم تنظيف الـ intervals كان يؤدي إلى تسرب ذاكرة بمعدل 2 ميجابايت كل دقيقة، مما تسبب في تجمد المتصفح بعد 20 دقيقة من الاستخدام المستمر. إليك كيف يجب كتابة Custom Hook للتعامل مع العمليات الدورية بشكل صحيح:

javascript
function usePeriodicDataFetch(url, interval = 5000) {
 const [data, setData] = React.useState(null);
 const [error, setError] = React.useState(null);
 
 React.useEffect(() => {
 let isMounted = true;
 let intervalId;
 
 const fetchData = async () => {
 try {
 const resp await fetch(url);
 if (!response.ok) throw new Error('Network response was not ok');
 const result = await response.json();
 if (isMounted) setData(result);
 } catch (err) {
 if (isMounted) setError(err);
 }
 };
 
 // Fetch immediately
 fetchData();
 
 // Set up interval
 intervalId = setInterval(fetchData, interval);
 
 // Cleanup function
 return () => {
 isMounted = false;
 clearInterval(intervalId); // تنظيف الـ interval لمنع تسرب الذاكرة
 };
 }, [url, interval]); // التبعيات محددة
 
 return { data, error };
}

الـ Memory Leaks داخل Custom Hooks: القاتل الصامت

أحد أكثر المشاكل خطورة التي واجهناها مع Custom Hooks هي تسرب الذاكرة. المشكلة ليست في الـ Hook نفسه، بل في كيفية استخدامه. عندما تنشئ اشتراكاً خارجياً داخل Custom Hook (مثل اشتراك WebSocket أو Event Listener)، فإنك تحتاج إلى تنظيف هذا الاشتراك عندما يتم إلغاء تحميل المكون. إذا لم تفعل ذلك، فإن الاشتراك سيبقى نشطاً في الذاكرة حتى بعد اختفاء المكون، مما يؤدي إلى تسرب الذاكرة. في مشروعنا مع منصة التداول المالي، كان لدينا Custom Hook اسمه useMarketData يستخدم WebSocket لتلقي تحديثات السوق اللحظية. المشكلة كانت في أننا لم نقم بتنظيف اشتراك WebSocket عند إلغاء تحميل المكون، مما أدى إلى تراكم آلاف الاشتراكات غير النشطة في الذاكرة. بعد 30 دقيقة من الاستخدام المستمر، كان المتصفح يستهلك أكثر من 1 جيجابايت من الذاكرة!

الحل؟ استخدام نمط الـ cleanup داخل useEffect. لكن حتى هذا ليس كافياً في بعض الحالات المعقدة. مثلاً، إذا كان لديك Custom Hook يستخدم عدة اشتراكات خارجية، فإنك تحتاج إلى تنظيف كل اشتراك بشكل منفصل. إليك مثال على Custom Hook يتعامل مع WebSocket بشكل صحيح:

javascript
function useWebSocket(url) {
 const [data, setData] = React.useState(null);
 const [error, setError] = React.useState(null);
 const socketRef = React.useRef();
 
 React.useEffect(() => {
 // إنشاء اتصال WebSocket
 const socket = new WebSocket(url);
 socketRef.current = socket;
 
 // إعداد المستمعين
 socket. () => {
 console.log('WebSocket connection established');
 };
 
 socket.onmessage = (event) => {
 setData(JSON.parse(event.data));
 };
 
 socket.onerror = (event) => {
 setError(new Error('WebSocket error'));
 };
 
 socket.onclose = () => {
 console.log('WebSocket connection closed');
 };
 
 // تنظيف الاتصال عند إلغاء تحميل المكون
 return () => {
 if (socketRef.current) {
 socketRef.current.close(); // إغلاق الاتصال لمنع تسرب الذاكرة
 socketRef.current = null;
 }
 };
 }, [url]); // إعادة إنشاء الاتصال عند تغير الـ URL
 
 return { data, error };
}

Custom Hooks في العالم الحقيقي: دراسة حالة من تويتر

في عام 2021، نشر فريق هندسة الواجهة في تويتر مقالاً تقنياً عن كيفية إعادة هيكلة نظام إدارة الحالة في تطبيقهم باستخدام Custom Hooks. المشكلة التي واجهوها كانت مشابهة لما واجهناه في مشاريعنا: مكونات عملاقة تحتوي على منطق متكرر، وصعوبة في تتبع التبعيات داخل useEffect، وتسرب الذاكرة بسبب الاشتراكات الخارجية. الحل الذي تبنوه كان إنشاء مجموعة من Custom Hooks المتخصصة لكل جزء من منطق التطبيق. مثلاً، بدلاً من كتابة منطق إدارة التغريدات داخل كل مكون، قاموا بإنشاء Custom Hook اسمه useTweet الذي يتعامل مع كل شيء يتعلق بالتغريدات: جلب البيانات، إدارة الحالة، التعامل مع الأخطاء، وحتى منطق إعادة المحاولة عند فشل الطلبات.

أحد الأشياء المثيرة للاهتمام في نهج تويتر هو أنهم استخدموا Custom Hooks ليس فقط لإعادة استخدام المنطق، بل أيضاً لفصل الاهتمامات بشكل أفضل. مثلاً، لديهم Custom Hook اسمه useInfiniteScroll مسؤول عن منطق التحميل اللانهائي، وآخر اسمه useOptimisticUpdates للتعامل مع التحديثات المتفائلة. هذا الفصل سمح لهم بتحسين أداء التطبيق بشكل كبير، حيث أصبح بإمكان React تتبع التبعيات بشكل أكثر دقة وتجنب إعادة التقديمات غير الضرورية. النتيجة؟ انخفاض بنسبة 30% في وقت تحميل الصفحة الأولي، وانخفاض بنسبة 45% في عدد إعادة التقديمات غير الضرورية.

مثال عملي: Custom Hook لإدارة النماذج المعقدة

في أحد مشاريعنا مع شركة تأمين، كان لدينا نموذج طلب تأمين يحتوي على أكثر من 50 حقلاً، مع منطق تحقق معقد يعتمد على قيم الحقول الأخرى. بدلاً من كتابة كل هذا المنطق داخل المكون، قمنا بإنشاء Custom Hook اسمه useInsuranceForm الذي يتعامل مع كل شيء يتعلق بالنموذج: إدارة الحالة، التحقق من الصحة، إرسال البيانات، والتعامل مع الأخطاء. هذا الـ Hook لم يجعل الكود أكثر نظافة فحسب، بل حسن أيضاً من أداء التطبيق بشكل كبير، حيث أصبح بإمكان React تتبع التبعيات بشكل أكثر دقة وتجنب إعادة التقديمات غير الضرورية.

javascript
function useInsuranceForm(initialValues) {
 const [values, setValues] = React.useState(initialValues);
 const [errors, setErrors] = React.useState({});
 const [isSubmitting, setIsSubmitting] = React.useState(false);
 
 // التحقق من صحة الحقول
 const validate = React.useCallback(() => {
 const newErrors = {};
 
 // مثال: التحقق من أن العمر أكبر من 18
 if (values.age < 18) {
 newErrors.age = 'يجب أن يكون العمر أكبر من 18';
 }
 
 // مثال: التحقق من أن البريد الإلكتروني صالح
 if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(values.email)) {
 newErrors.email = 'البريد الإلكتروني غير صالح';
 }
 
 setErrors(newErrors);
 return Object.keys(newErrors).length === 0;
 }, [values]); // التبعيات محددة
 
 // التعامل مع تغيير قيم الحقول
 const handleChange = React.useCallback((e) => {
 const { name, value } = e.target;
 setValues(prev => ({ ...prev, [name]: value }));
 }, []);
 
 // إرسال النموذج
 const handleSubmit = React.useCallback(async (e) => {
 e.preventDefault();
 if (!validate()) return;
 
 setIsSubmitting(true);
 try {
 const resp await fetch('/api/submit-insurance', {
 method: 'POST',
 headers: { 'Content-Type': 'application/json' },
 body: JSON.stringify(values),
 });
 
 if (!response.ok) throw new Error('فشل إرسال النموذج');
 // التعامل مع الاستجابة الناجحة
 } catch (error) {
 setErrors(prev => ({ ...prev, submit: error.message }));
 } finally {
 setIsSubmitting(false);
 }
 }, [values, validate]);
 
 return {
 values,
 errors,
 isSubmitting,
 handleChange,
 handleSubmit,
 };
}

متى يجب (ومتى لا يجب) استخدام Custom Hooks

ليس كل منطق يحتاج إلى Custom Hook. في الواقع، استخدام Custom Hooks للمهام البسيطة يمكن أن يضيف تعقيداً غير ضروري إلى الكود. القاعدة الذهبية هي: إذا كان المنطق يستخدم أكثر من مرة واحدة، أو إذا كان معقداً بما يكفي لجعل المكون غير قابل للقراءة، فإن Custom Hook هو الحل المناسب. مثلاً، منطق إدارة النماذج، التعامل مع البيانات من API، أو إدارة الاشتراكات الخارجية هي حالات مثالية لاستخدام Custom Hooks. من ناحية أخرى، إذا كان لديك منطق بسيط يستخدم مرة واحدة فقط داخل مكون واحد، فإن استخدام Custom Hook قد يكون مبالغة.

هناك أيضاً حالات يجب فيها تجنب Custom Hooks تماماً. مثلاً، إذا كان الـ Hook يعتمد على سياق محدد جداً لا يمكن إعادة استخدامه في أماكن أخرى، أو إذا كان الـ Hook يحتوي على منطق يعتمد على DOM بشكل مباشر (مثل التعامل مع عناصر محددة باستخدام document.getElementById)، فإن استخدام Custom Hook قد لا يكون الخيار الأفضل. في هذه الحالات، من الأفضل الاحتفاظ بالمنطق داخل المكون نفسه أو استخدام نمط آخر مثل Higher-Order Components.

  • •استخدم Custom Hooks عندما: تريد إعادة استخدام منطق بين عدة مكونات، أو عندما يكون المنطق معقداً بما يكفي لجعل المكون غير قابل للقراءة.
  • •تجنب Custom Hooks عندما: يكون المنطق بسيطاً ويستخدم مرة واحدة فقط، أو عندما يعتمد على سياق محدد جداً لا يمكن إعادة استخدامه.
  • •كن حذراً مع Custom Hooks التي: تتعامل مع DOM مباشرة، أو تحتوي على اشتراكات خارجية تحتاج إلى تنظيف، أو تستخدم عمليات I/O ثقيلة قد توقف الـ Event Loop.
  • •دائماً قم بقياس الأداء: استخدم أدوات مثل React DevTools وChrome Performance Tab لقياس تأثير Custom Hooks على أداء التطبيق قبل وبعد إضافتها.

خلاصة المهندس: نصائح من الخنادق البرمجية

بعد أكثر من عشر سنوات في كتابة React، وإدارة مشاريع عملاقة مع فرق متعددة الجنسيات، هذه هي النصائح التي أتمنى لو عرفتها عندما بدأت باستخدام Custom Hooks:

  • •دائماً حدد التبعيات في useEffect وuseCallback بدقة. عدم تحديد التبعيات بشكل صحيح هو السبب الأول لتسرب الذاكرة وإعادة التقديمات غير الضرورية.
  • •استخدم useMemo داخل Custom Hooks لتجنب إعادة إنشاء الكائنات والمصفوفات في كل دورة تحديث. لكن لا تبالغ في استخدامه - إذا كانت العملية الحسابية بسيطة، فقد لا يكون هناك حاجة لـ useMemo.
  • •دائماً قم بتنظيف الاشتراكات الخارجية داخل return function من useEffect. هذا يشمل WebSockets، Event Listeners، intervals، وtimeouts.
  • •استخدم React DevTools لتحليل أداء Custom Hooks. ابحث عن المكونات التي تعيد تقديم نفسها بشكل غير ضروري، وتحقق من سبب ذلك.
  • •لا تجعل Custom Hooks كبيرة جداً. إذا كان الـ Hook يحتوي على أكثر من 100 سطر من الكود، فربما حان الوقت لتقسيمه إلى عدة Hooks أصغر وأكثر تخصصاً.
  • •اختبر Custom Hooks بشكل مستقل باستخدام مكتبات مثل react-hooks-testing-library. هذا يضمن أن الـ Hook يعمل كما هو متوقع حتى لو تغيرت المكونات التي تستخدمه.
  • •تجنب الاعتماد على سياق خارجي داخل Custom Hooks ما لم يكن ذلك ضرورياً للغاية. الاعتماد على سياق خارجي يجعل الـ Hook أقل قابلية لإعادة الاستخدام وأكثر عرضة للأخطاء عند تغيير السياق.
  • •استخدم TypeScript مع Custom Hooks. هذا يضيف طبقة إضافية من الأمان ويجعل الكود أكثر قابلية للصيانة، خاصة في المشاريع الكبيرة.

في النهاية، Custom Hooks في React ليست مجرد أداة لتنظيم الكود، بل هي نمط برمجي كامل يغير طريقة تفكيرك في إدارة الحالة والمنطق. عندما تستخدمها بشكل صحيح، يمكنها تحسين أداء التطبيق وقابليته للصيانة بشكل كبير. لكن عندما تستخدمها بشكل خاطئ، يمكنها أن تتحول إلى كابوس من تسرب الذاكرة وإعادة التقديمات غير الضرورية. المفتاح هو فهم ما يحدث خلف الكواليس في React Fiber Tree وEvent Loop، واختبار أداء التطبيق باستمرار باستخدام الأدوات المناسبة. تذكر: ليس كل منطق يحتاج إلى Custom Hook، وليس كل Custom Hook هو حل سحري. استخدمها بحكمة، واختبر دائماً، وقس الأداء باستمرار.

React Hooks JavaScript Frontend Development Web Performance Custom Hooks

التعليقات

العودة للمقالات
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر