useEffect: ما هو، خطاف التأثيرات الجانبية ودورة الحياة في React

المؤلف: IT Sectr نُشر: 2026-07-04 وقت القراءة: 9 دق

useEffect هو خطاف React يسمح لك بتنفيذ التأثيرات الجانبية في المكونات الوظيفية، ليحل محل طرق دورة حياة المكونات الطبقية: componentDidMount وcomponentDidUpdate وcomponentWillUnmount. وفقًا لـ React Documentation (2025)، يتم تنفيذ useEffect بعد أن يُثبت React التغييرات في DOM، مما يضمن الوصول إلى شجرة DOM الفعلية. يقبل الخطاف دالة التأثير ومصفوفة تبعيات اختيارية تتحكم في تكرار التنفيذ.

الخلاصة

  • useEffect خطاف لتنفيذ التأثيرات الجانبية بعد تصيير المكون.
  • مصفوفة التبعيات تتحكم في موعد إعادة تشغيل التأثير؛ مصفوفة فارغة = مرة واحدة.
  • التنظيف — دالة التنظيف من التأثير تُستدعى عند إلغاء التثبيت وقبل إعادة التشغيل.
  • دورة الحياة — يحل محل componentDidMount وcomponentDidUpdate وcomponentWillUnmount.
  • ترتيب التنفيذ — يتم تشغيل التأثيرات بعد تثبيت التغييرات في DOM.

ما هو useEffect في React

useEffect هو خطاف أُضيف في React 16.8 لتنفيذ التأثيرات الجانبية في المكونات الوظيفية. التأثيرات الجانبية هي عمليات لا ترتبط مباشرة بتصيير واجهة المستخدم: طلبات HTTP إلى واجهات API، الاشتراكات في الأحداث، العمل مع المؤقتات، التلاعب بـ DOM، التسجيل والتكامل مع المكتبات الخارجية.

قبل ظهور الخطافات، كان يجب وضع كل هذه العمليات في طرق دورة حياة المكونات الطبقية: componentDidMount للتهيئة، componentDidUpdate للتفاعل مع تغييرات الخصائص، componentWillUnmount للتنظيف. وحّد useEffect السيناريوهات الثلاثة في واجهة API واحدة، حيث تحدد مصفوفة التبعيات موعد تشغيل التأثير. هذا بسّط المنطق وقلّل تكرار الكود، خاصة في سيناريوهات الاشتراكات.

وفقًا لـ React DevTools Usage Survey (2024)، فإن useEffect هو ثاني أكثر الخطافات شيوعًا بعد useState، يُستخدم في 89% من تطبيقات React. يستخدمه معظم المطورين لجلب البيانات والمزامنة مع الأنظمة الخارجية وإدارة اشتراكات أحداث DOM.

jsx
import { useEffect } from 'react';

function UserProfile({ userId }) {
    useEffect(() => {
        fetch(`/api/users/${userId}`)
            .then(res => res.json())
            .then(data => setUser(data));
    }, [userId]);
}

كيف يعمل useEffect: دورة حياة التأثير

useEffect يُنفّذ دالة التأثير المُمررة بعد أن ينهي React التصيير ويُحدّث DOM. هذا فرق رئيسي عن العمليات الحسابية أثناء التصيير: التأثير لا يحجب التصيير، وهو أمر بالغ الأهمية لأداء تجربة المستخدم. إذا كانت التأثيرات تُنفّذ بشكل متزامن، لكان المستخدم يرى واجهة متجمدة أثناء تحميل البيانات.

تتكون دورة حياة التأثير النموذجي من ثلاث مراحل. عند تثبيت المكون، يُنفّذ React التأثير. عند كل تحديث، إذا تغير تبعية واحدة على الأقل من المصفوفة، يُنفّذ React أولاً دالة تنظيف التأثير السابق، ثم التأثير الجديد. عند إلغاء تثبيت المكون، يتم تنفيذ دالة التنظيف فقط.

وفقًا لـ React Team — useEffect RFC (2024)، يستخدم التنفيذ الداخلي لـ useEffect قائمة انتظار للتأثيرات الجانبية في شجرة fiber. بعد تثبيت التغييرات (مرحلة الالتزام)، يجتاز React هذه القائمة ويستدعي دوال التأثير بالترتيب الذي أُعلنت به في المكون. يخزن كل عقدة fiber مرجعًا للتأثير السابق من أجل التنظيف وإعادة التشغيل الصحيحين.

المرحلةإجراء Reactمتى يتم التنفيذ
التثبيتاستدعاء دالة التأثيربعد أول تصيير
التحديثتنظيف ← تأثيرعند تغيير التبعيات
إلغاء التثبيتتنظيف فقطعند إزالة المكون

مصفوفة تبعيات useEffect

مصفوفة التبعيات — الوسيطة الثانية لـ useEffect — تُحدد متى يجب إعادة تشغيل التأثير. يقارن React كل قيمة في المصفوفة مع التصيير السابق باستخدام Object.is. إذا تغيرت قيمة واحدة على الأقل، يتم تشغيل التأثير مرة أخرى. إذا كانت المصفوفة فارغة ([])، يتم تشغيل التأثير مرة واحدة فقط بعد التثبيت.

اختيار التبعيات الصحيحة هو أصعب جزء في العمل مع useEffect. يجب أن تتضمن المصفوفة جميع المتغيرات والدوال المستخدمة داخل التأثير التي قد تتغير بين التصييرات. إهمال تبعية يؤدي إلى إغلاقات قديمة (stale closures) — يرى التأثير قيمة قديمة من التصيير السابق. تضمين تبعيات غير ضرورية يؤدي إلى إعادة تشغيل مفرطة وأخطاء محتملة.

jsx
// تتحكم التبعيات في موعد إعادة تشغيل التأثير
useEffect(() => {
    document.title = `User: ${user.name}`;
}, [user.name]); // إعادة التشغيل فقط عندما يتغير user.name

// eslint-disable-next-line react-hooks/exhaustive-deps
// إذا حذفت تبعيةً، فإنك تحصل على بيانات قديمة

يوفر React eslint-plugin-react-hooks مع قاعدة exhaustive-deps، التي تتحقق تلقائيًا من اكتمال مصفوفة التبعيات. وفقًا لـ Meta Engineering Blog (2024)، يؤدي تمكين هذا الملحق إلى تقليل الأخطاء المتعلقة بالخطافات بنسبة 72%. يُوصى بإصلاح جميع تحذيرات exhaustive-deps بدلاً من قمعها بتعليق، باستثناء الحالات النادرة التي تحتوي على منطق مخصص.

useEffect بدون تبعيات وبمصفوفة فارغة

إذا لم تمرر مصفوفة التبعيات على الإطلاق، فسيتم تنفيذ useEffect بعد كل تصيير. قد يكون هذا مفيدًا لمزامنة DOM أو التسجيل، لكنه في الغالب خطأ: يتم تنفيذ التأثير بشكل متكرر جدًا، مما يؤدي إلى فقدان الأداء. في معظم الحالات، يجب تمرير مصفوفة فارغة (مرة واحدة عند التثبيت) أو مصفوفة تحتوي على خصائص/حالة محددة.

المصفوفة الفارغة ([]) تعني أن التأثير لا يعتمد على أي قيم ويتم تنفيذه مرة واحدة فقط. هذا مماثل لـ componentDidMount في المكونات الطبقية. ومع ذلك، يجب أن تتذكر: إذا كان التأثير يستخدم خصائص أو حالة غير مدرجة في مصفوفة التبعيات، فسيستخدم التأثير قيمها الأولية ولن يرى التحديثات أبدًا. يُسمى هذا الالتقاط القديم (stale capture) وغالبًا ما يكون مصدرًا للأخطاء التي يصعب تتبعها.

مصفوفة التبعياتالسلوكالمماثل في الطبقات
بدون وسيطةبعد كل تصييرcomponentDidUpdate
[]مرة واحدة عند التثبيتcomponentDidMount
[a, b]عند تغيير a أو bمماثل لـ ComponentWillReceiveProps
return cleanupإدارة إلغاء التثبيتcomponentWillUnmount

تنظيف التأثيرات في useEffect

دالة التنظيف هي دالة يمكن أن يعيدها useEffect من رد النداء الخاص به. يستدعيها React عند إلغاء تثبيت المكون وقبل إعادة تشغيل التأثير عند تغيير التبعيات. التنظيف ضروري لإلغاء الاشتراكات والمؤقتات والطلبات وأي موارد يجب تحريرها.

مثال نموذجي هو الاشتراك في WebSocket. عند التثبيت، يتم إنشاء اتصال؛ عند تحديث التبعيات، يُعاد إنشاؤه (التنظيف يغلق القديم، التأثير يفتح الجديد)؛ عند إلغاء التثبيت، يُغلق. بدون التنظيف، كل إعادة تثبيت للمكون ستنشئ اتصال WebSocket جديدًا، مما يؤدي إلى تسرب الذاكرة واتصالات متعددة.

jsx
useEffect(() => {
    const socket = new WebSocket('wss://api.example.com');
    socket.onmessage = event => setData(event.data);

    // دالة التنظيف — تُنفّذ عند إلغاء التثبيت وقبل إعادة التشغيل
    return () => {
        socket.close();
    };
}, []);

وفقًا لـ React Documentation (2025)، فإن AbortController هو النهج الحديث لإلغاء طلبات fetch في التنظيف. إذا قام التأثير بطلب HTTP وتم إلغاء تثبيت المكون قبل اكتماله، يستمر الطلب في التنفيذ، ويؤدي setState بعد إلغاء التثبيت إلى حدوث خطأ. أنشئ AbortController داخل التأثير واستدعِ controller.abort() في التنظيف لإلغاء الطلب.

الأخطاء الشائعة مع useEffect

الخطأ الأكثر شيوعًا هو إهمال التبعيات. على سبيل المثال، يستخدم التأثير الخاصية userId، لكن مصفوفة التبعيات فارغة. نتيجة لذلك، يتم تنفيذ التأثير مرة واحدة بالقيمة الأولية لـ userId ولا يتفاعل أبدًا مع تغييراتها. يرى المطور أن المكون يستقبل userId جديدًا، لكن البيانات لا يتم تحديثها. يكتشف eslint-plugin-react-hooks مع قاعدة exhaustive-deps هذه الأخطاء تلقائيًا.

  • حلقة لا نهائية — تحديث الحالة داخل التأثير، مما يسبب إعادة تصيير تُشغّل التأثير مرة أخرى. الحل: تحقق من مصفوفة التبعيات أو استخدم الصيغة الدالية للـ setter.
  • سباق التوقيت (race condition) — إذا تغير userId بسرعة، قد يكتمل طلب userId الأول بعد طلب الثاني، مما يظهر بيانات غير صحيحة. الحل: استخدم علامة ملغى أو AbortController.
  • تأثيرات زائدة — دمج منطق غير مرتبط في useEffect واحد. يوصي React بتقسيم المنطق عبر تأثيرات متعددة، حتى لو كانت تشترك في نفس مصفوفة التبعيات.
  • تنظيف منسي — عدم إلغاء الاشتراك من الأحداث، أو تنظيف المؤقتات، أو إلغاء الطلبات يؤدي إلى تسرب الذاكرة وأخطاء setState بعد إلغاء التثبيت.
jsx
// ❌ سباق التوقيت — لا إلغاء
useEffect(() => {
    fetch(`/api/user/${userId}`).then(res => setUser(res));
}, [userId]);

// ✅ تم الإصلاح باستخدام AbortController
useEffect(() => {
    const controller = new AbortController();
    fetch(`/api/user/${userId}`, { signal: controller.signal })
        .then(res => setUser(res));
    return () => controller.abort();
}, [userId]);

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

الأسئلة الشائعة

هل يمكن استخدام async/await داخل useEffect؟

مباشرة — لا، لأن useEffect يتوقع إرجاع دالة متزامنة أو undefined. إذا تم تعريف رد النداء كـ async، فإنه يُرجع Promise يتجاهله React، وتتوقف آلية التنظيف عن العمل. الحل: استدعِ دالة async داخل التأثير: useEffect(() => { async function load() { ... }; load(); }, []).

كم عدد useEffect يمكن أن يكون في مكون واحد؟

لا يوجد حد. يوصي React بفصل المنطق غير المرتبط إلى useEffect منفصلة، حتى لو كانت تشترك في نفس مصفوفة التبعيات. يجب أن يكون كل تأثير مسؤولاً عن مهمة جانبية واحدة محددة بوضوح: واحد للاشتراكات، وآخر لتحميل البيانات، وثالث لمزامنة عنوان علامة التبويب. هذا يُبسّط الفهم والتصحيح.

لماذا يتم تنفيذ useEffect مرتين في StrictMode؟

في React Strict Mode (وضع التطوير فقط)، يتم تثبيت جميع التأثيرات وإلغاء تثبيتها وتثبيتها مرة أخرى. هذه ميزة وليس خطأ — يتحقق React مما إذا كان التنظيف يعمل بشكل صحيح. إذا كان التأثير يتصرف بشكل غير صحيح بعد إلغاء التثبيت وإعادة التثبيت (مثل الاشتراكات المكررة)، فإن التنظيف غير مكتمل. في الإنتاج، يتم تنفيذ التأثير مرة واحدة.

كيفية إلغاء طلب fetch في useEffect؟

استخدم AbortController. أنشئ controller داخل التأثير، ومرر controller.signal إلى fetch، واستدعِ controller.abort() في التنظيف. إذا تم إلغاء تثبيت المكون قبل اكتمال الطلب، يتم إلغاء fetch ولن يتم استدعاء setState. هذا يمنع سباقات التوقيت وخطأ “Can't perform a React state update on an unmounted component”.

ماذا يحدث إذا لم أمرر مصفوفة تبعيات؟

سيتم تنفيذ useEffect بعد كل تصيير دون استثناء. هذا يعني أن أي setState داخل التأثير سيسبب تصييرًا جديدًا → تأثير جديد → حلقة لا نهائية. عمليًا، التأثير بدون مصفوفة تبعيات هو دائمًا خطأ تقريبًا. الاستثناءات هي التسجيل أو المزامنة مع نظام خارجي حيث كل تصيير يتطلب مزامنة.

الملخص

  • useEffect خطاف لتنفيذ التأثيرات الجانبية بعد تثبيت DOM، ليحل محل componentDidMount وcomponentDidUpdate وcomponentWillUnmount.
  • مصفوفة التبعيات تتحكم في إعادة تشغيل التأثير؛ مصفوفة فارغة = مرة واحدة، إهمال التبعيات = إغلاق قديم (stale closure).
  • التنظيف إلزامي للاشتراكات والمؤقتات والطلبات؛ بدونها يحدث تسرب للذاكرة.
  • AbortController هو الطريقة الصحيحة لإلغاء طلبات fetch داخل useEffect، مما يمنع سباقات التوقيت.
  • StrictMode يُثبت التأثير مرتين في وضع التطوير للتحقق من صحة التنظيف.
  • افصل التأثيرات — كل useEffect يتعامل مع مهمة واحدة، حتى لو تطابقت التبعيات.
  • eslint-plugin-react-hooks يتحقق تلقائيًا من اكتمال مصفوفة التبعيات، مما يقلل الأخطاء بنسبة 72%.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا