تخطَّ إلى المحتوى

٢١ يوليو ٢٠٢٦

الفرق بين useEffect و useLayoutEffect في React بالعربي

شارك المقال:

كلا الخطّافين لهما نفس الصياغة تقريبًا — نفس الوسيطين (setup, dependencies)، ونفس فكرة دالة التنظيف (cleanup). فلماذا يوجد اثنان؟ الفرق كله يتلخّص بكلمة واحدة: التوقيت.

الفرق بالتوقيت بالضبط

متى يعمليحجب رسم المتصفح؟
useEffectبعد أن يرسم المتصفح الشاشة فعليًالا
useLayoutEffectبعد تحديث الـ DOM، لكن قبل أن يرسم المتصفح أي شيءنعم

بمعنى آخر: React يحدّث الـ DOM دائمًا أولًا (لكلا الحالتين)، لكن مع useLayoutEffect يتوقّف قبل أن يسمح للمتصفح بطلاء (paint) هذا التحديث على الشاشة، وينتظر أن ينتهي الكود بداخله — بما فيه أي تحديث حالة يسبّبه.

المشكلة العملية: الوميض البصري (Flicker)

المثال الكلاسيكي: تلميح (Tooltip) يجب أن يظهر فوق عنصر ما، وتحتاج قياس ارتفاعه أولًا لتحديد موضعه بدقة.

// ❌ باستخدام useEffect: المستخدم يرى وميضًا سريعًا
function Tooltip() {
  const ref = useRef(null);
  const [top, setTop] = useState(0);

  useEffect(() => {
    const { height } = ref.current.getBoundingClientRect();
    setTop(-height - 8);
  }, []);

  return <div ref={ref} style={{ position: "absolute", top }}>تلميح</div>;
}

هنا يحصل التالي: React يرسم العنصر بموضعه الأولي (top: 0) → المتصفح يطلي هذا الموضع فعليًا على الشاشة → بعدها فقط يعمل useEffect ويقيس الارتفاع ويحدّث top → رندر ثانٍ → المتصفح يطلي الموضع الصحيح. النتيجة: المستخدم يرى قفزة سريعة من الموضع الخطأ للموضع الصحيح — وميض ملحوظ خصوصًا على الأجهزة البطيئة.

// ✅ باستخدام useLayoutEffect: لا وميض إطلاقًا
function Tooltip() {
  const ref = useRef(null);
  const [top, setTop] = useState(0);

  useLayoutEffect(() => {
    const { height } = ref.current.getBoundingClientRect();
    setTop(-height - 8);
  }, []);

  return <div ref={ref} style={{ position: "absolute", top }}>تلميح</div>;
}

بنفس الكود تمامًا لكن مع useLayoutEffect: React يحدّث الـ DOM بالموضع الأولي، لكن قبل أن يسمح للمتصفح بطلاء أي شيء، يُنفَّذ الخطّاف ويقيس الارتفاع ويحدّث الحالة — فيُعاد رسم DOM بالموضع الصحيح، وعندها فقط يطلي المتصفح النتيجة النهائية دفعة واحدة. المستخدم لا يرى إلا الموضع الصحيح مباشرة.

متى تستخدم كل واحد؟

  • استخدم useEffect كافتراضي دائمًا — جلب بيانات، اشتراكات، مؤقّتات، أي تأثير جانبي لا يعتمد على قياس بصري فوري. لا يحجب رسم الصفحة، فتجربة المستخدم أسرع.
  • انتقل لـ useLayoutEffect فقط عندما ترى وميضًا فعليًا ناتجًا عن قياس DOM (أبعاد، موضع، تمرير scroll) وتحديث حالة بناءً عليه قبل أن يرى المستخدم أي شيء.

💡 التوثيق الرسمي لـ React يفتح صفحة useLayoutEffect بتحذير صريح: يفضَّل useEffect كلما أمكن. useLayoutEffect أداة لحالة ضيّقة ومحدّدة، لا بديل عام لـ useEffect.

قيد إضافي: لا يعمل على الخادم

useLayoutEffect لا يُنفَّذ إطلاقًا أثناء التصيير من الخادم (SSR) — لا توجد معلومات تخطيط (Layout) أصلًا هناك. إن كان مكوّنك يُرسَم على الخادم وتحتاج هذا النمط، الحلول الشائعة: تأجيل قياس الـ Layout إلى بعد التركيب في المتصفح، أو تعليم المكوّن كأنه يعمل في المتصفح فقط.

خلاصة

الاثنان يشتركان بنفس الصياغة، والفرق الوحيد الحقيقي هو التوقيت: useEffect يعمل بعد الرسم فلا يحجب شيئًا، وuseLayoutEffect يعمل قبل الرسم فيحجب المتصفح حتى ينتهي. ابدأ دائمًا بـ useEffect، وانتقل لـ useLayoutEffect فقط عندما تحل مشكلة وميض فعلية ترها بعينك.

اقرأ الشرح الكامل في درس useEffect ومرجع useLayoutEffect من مسار React بالعربي، وإن كنت تواجه حلقة تحديثات لا تتوقف بدل الوميض، راجع حل حلقة useEffect اللانهائية.

📚 مصادر رسمية للتعمّق: freeCodeCamp — مصدر تعلّم البرمجة

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

هل useLayoutEffect أبطأ من useEffect؟

من ناحية تقنية، useLayoutEffect يحجب المتصفح عن رسم الشاشة إلى أن ينتهي — بينما useEffect لا يحجب شيئًا لأنه يعمل بعد الرسم أصلًا. لهذا التوثيق الرسمي يحذّر من استخدامه بكثرة: كود بطيء داخل useLayoutEffect يُبطئ ظهور الصفحة فعليًا للمستخدم، بينما نفس الكود داخل useEffect لا يؤخّر الرسم الأول.

هل useLayoutEffect يعمل في مكوّنات الخادم (Server Components) أو أثناء SSR؟

لا. useLayoutEffect لا يعمل إطلاقًا أثناء التصيير من الخادم لأن معلومات التخطيط (Layout) غير موجودة هناك أصلًا — لا يوجد متصفح يقيس شيئًا. إن استخدمته في مكوّن يُرسَم على الخادم قد تظهر تحذيرات؛ الحل الشائع تأجيل المنطق أو التأكد من أن المكوّن يعمل في المتصفح فقط.

إذا لم أشعر بأي وميض، هل يعني هذا أن useEffect كافٍ دائمًا؟

غالبًا نعم. القاعدة العملية: ابدأ دائمًا بـ useEffect. لا تنتقل لـ useLayoutEffect إلا إذا رأيت بعينك مشكلة وميض فعلية ناتجة عن قياس عنصر DOM بعد رسمه وتعديل حالة بناءً عليه — وهذه حالة أضيق بكثير مما يتخيّله كثير من المبرمجين.

اقرأ أيضًا

تصفّح كل المقالات في المدوّنة، أو ابدأ التعلّم من المسارات و خرائط الطريق.