البرمجة مبتدئ 9 دقيقة

كيفية جلب البيانات في React باستخدام useEffect

جلب البيانات في React أمر مباشر بمجرد فهم دورة الحياة. يعمل useEffect بعد الرسم، تُطلق طلب fetch، وعندما يتم الـ Promise تحدّث الـ state. لكن التفاصيل مهمة: معالجة حالات التحميل والخطأ، منع تحديث state على مكوّنات غير مُركّبة، ومعرفة متى تتوقف عن كتابة هذا الكود الاعتيادي وتلجأ إلى React Query.

الخطوات

  1. 1

    إعداد الحالات الأساسية الثلاث

    يحتاج كل مكوّن يجلب بيانات إلى ثلاث حالات: البيانات نفسها، وعلامة التحميل، والخطأ. ابدأ بهذه قبل كتابة أي سطر من كود fetch. تجنّب الرغبة في دمجها في كائن واحد مبكراً — ثلاث استدعاءات useState منفصلة أوضح.

    jsx
    import { useState, useEffect } from 'react';
    
    function UserProfile({ userId }) {
      const [user,    setUser]    = useState(null);
      const [loading, setLoading] = useState(true);
      const [error,   setError]   = useState(null);
    
      // منطق الجلب يأتي بعد ذلك
    }
  2. 2

    كتابة useEffect الأساسي للجلب

    استدعِ useEffect مع مصفوفة تبعيات فارغة ([]) للتشغيل مرة واحدة بعد التركيب. لا تجعل دالة callback للتأثير async — React لا تتعامل مع الـ Promise المُعادة. عرّف دالة async داخلية واستدعِها فوراً.

    jsx
    useEffect(() => {
      async function fetchUser() {
        try {
          const res  = await fetch(`/api/users/${userId}`);
          if (!res.ok) throw new Error(`HTTP ${res.status}`);
          const data = await res.json();
          setUser(data);
        } catch (err) {
          setError(err.message);
        } finally {
          setLoading(false);
        }
      }
    
      fetchUser();
    }, []); // تعمل مرة واحدة بعد التركيب
  3. 3

    رسم الحالات الثلاث

    يجب أن يعالج JSX الخاص بك الحالات الثلاث صراحةً. مؤشر تحميل لا يختفي أبداً، أو شاشة بيضاء عند الخطأ، كلاهما أخطاء. عالجهما من البداية.

    jsx
    if (loading) return <p>جاري التحميل...</p>;
    if (error)   return <p>خطأ: {error}</p>;
    if (!user)   return null;
    
    return (
      <div>
        <h1>{user.name}</h1>
        <p>{user.email}</p>
      </div>
    );
  4. 4

    إلغاء الطلب عند إزالة المكوّن

    إذا أُزيل المكوّن قبل اكتمال الطلب — انتقل المستخدم بعيداً — سيكتمل الطلب ويستدعي setUser على مكوّن غير مُركّب. هذا يسبب تحذير React وفي بعض الحالات أخطاء. استخدم AbortController لإلغاء الطلب في دالة التنظيف.

    jsx
    useEffect(() => {
      const controller = new AbortController();
    
      async function fetchUser() {
        try {
          const res  = await fetch(`/api/users/${userId}`, {
            signal: controller.signal,
          });
          if (!res.ok) throw new Error(`HTTP ${res.status}`);
          const data = await res.json();
          setUser(data);
        } catch (err) {
          if (err.name === 'AbortError') return; // تجاهل — إلغاء متعمد
          setError(err.message);
        } finally {
          setLoading(false);
        }
      }
    
      fetchUser();
    
      return () => controller.abort(); // تنظيف: إلغاء عند الإزالة
    }, [userId]);
  5. 5

    إعادة الجلب عند تغير التبعية

    ضع أي متغير يعتمد عليه التأثير في مصفوفة التبعيات. هنا userId هو prop — عند تغيّره (تصفّح المستخدم لملف شخصي آخر)، يُعاد تشغيل التأثير لجلب البيانات الصحيحة. أعد ضبط حالة التحميل والخطأ في بداية كل تشغيل جلب.

    jsx
    useEffect(() => {
      const controller = new AbortController();
    
      setLoading(true);  // إعادة الضبط عند كل userId جديد
      setError(null);
    
      async function fetchUser() {
        try {
          const res  = await fetch(`/api/users/${userId}`, {
            signal: controller.signal,
          });
          if (!res.ok) throw new Error(`HTTP ${res.status}`);
          setUser(await res.json());
        } catch (err) {
          if (err.name !== 'AbortError') setError(err.message);
        } finally {
          setLoading(false);
        }
      }
    
      fetchUser();
      return () => controller.abort();
    }, [userId]); // يُعاد التشغيل عند تغيّر userId
  6. 6

    فهم الاستدعاء المزدوج في Strict Mode

    في بيئة التطوير، يقوم React 18 Strict Mode بتركيب كل مكوّن مرتين عمداً. سيُشغَّل useEffect مرتين. هذا متعمد — يكشف التأثيرات التي تفتقر إلى دالة تنظيف. مع AbortController في مكانه بشكل صحيح، يُلغى الطلب الأول وينجح الثاني فقط. هذا هو السلوك المتوقع. لا تتحايل على ذلك بتعطيل Strict Mode.

  7. 7

    متى تستخدم React Query بدلاً من ذلك

    بمجرد احتياج جلب البيانات إلى التخزين المؤقت، وإعادة الجلب في الخلفية، والصفحات، والتحديثات المتفائلة، أو إزالة التكرار، يصبح جلب useEffect المكتوب يدوياً عبئاً في الصيانة. React Query (أو SWR) يتعامل مع كل هذا وأكثر بواجهة API أصغر بكثير. المعيار تقريباً: إذا وجدت نفسك تكتب نفس نمط loading/error/refetch في أكثر من مكوّنين، الجأ إلى React Query.

    jsx
    // نفس الجلب مع React Query — لاحظ ما اختفى
    import { useQuery } from '@tanstack/react-query';
    
    function UserProfile({ userId }) {
      const { data: user, isLoading, error } = useQuery({
        queryKey: ['user', userId],
        queryFn:  () =>
          fetch(`/api/users/${userId}`)
            .then(res => { if (!res.ok) throw new Error(`HTTP ${res.status}`); return res.json(); }),
      });
    
      if (isLoading) return <p>جاري التحميل...</p>;
      if (error)     return <p>خطأ: {error.message}</p>;
    
      return <h1>{user.name}</h1>;
    }
    // React Query يتعامل تلقائياً مع: التخزين المؤقت، إزالة التكرار،
    // إعادة الجلب في الخلفية، الإلغاء عند الإزالة، إعادة المحاولة عند الفشل

نصائح ومحاذير

  • لا تجعل callback الخاصة بـ <code>useEffect</code> نفسها <code>async</code> أبداً. يجب أن تُعيد إما لا شيء أو دالة تنظيف — وليس Promise.
  • قاعدة lint لمصفوفة التبعيات (<code>react-hooks/exhaustive-deps</code>) موجودة لسبب وجيه. إذا كان ESLint يطلب منك إضافة شيء للمصفوفة، استمع إليه — فهو محق دائماً تقريباً.
  • إذا كنت بحاجة للجلب عند نقر زر (وليس عند التركيب)، لا تحتاج <code>useEffect</code> على الإطلاق. استدعِ دالة async مباشرة في معالج الحدث.
  • تجنب تخزين البيانات المشتقة في state. إذا كان بإمكانك حساب شيء من <code>user</code> في دالة الرسم، لا تضعه في <code>useState</code> منفصل.
  • للطلبات المتسلسلة (اجلب B فقط بعد اكتمال A)، سلسلها داخل <code>useEffect</code> واحد مع <code>AbortController</code> واحد — لا تتداخل استدعاءات <code>useEffect</code>.

خاتمة

النمط الكامل — ثلاث متغيرات state، ودالة async داخلية، وتنظيف AbortController، ومصفوفة تبعيات صحيحة — يغطي تقريباً كل حالة استخدام لجلب البيانات ستواجهها. الكود الاعتيادي يستحق الكتابة مرة واحدة لفهمه. بعد ذلك، React Query يُريحك من كتابته في كل مكوّن.

#React #Hooks #useEffect
العودة إلى جميع الأدلة

هل تحتاج مساعدة في مشروعك؟

احجز استشارة مجانية لمدة 30 دقيقة لمناقشة تحدياتك التقنية واستكشاف الحلول معًا.