Kodokon kodokon.com

استدعاء واجهة API: fetch، التحميل، والأخطاء

حمّل بيانات بعيدة باستخدام fetch داخل تأثير، مع نمذجة حالتَي التحميل والخطأ بشكل نظيف.

10 دقيقة · 3 أسئلة

افتح هذا الدرس في Kodokon

يستغرق طلب الشبكة وقتاً ويمكن أن يفشل: يجب أن تعكس واجهتك هذه الحقائق. العُرف المهني هو تتبع ثلاث قطع متمايزة من الحالة: البيانات المستقبَلة، ومنطقي (boolean) للـ التحميل، وخطأ محتمل. يُوضَع الطلب داخل useEffect، لأن الاتصال بخادم هو تأثير جانبي.

JSX
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
  fetch("https://api.example.com/users")
    .then((res) => {
      if (!res.ok) throw new Error("HTTP " + res.status);
      return res.json();
    })
    .then((data) => setUsers(data))
    .catch((err) => setError(err.message))
    .finally(() => setLoading(false));
}, []);
ثلاث حالات لطلب واحد: البيانات، التحميل، الخطأ.

في العرض، عالج الحالات بالترتيب: التحميل أولاً، ثم الخطأ، وأخيراً البيانات. هذه العودة المبكّرة (early returns) تحافظ على قابلية قراءة الشيفرة وتضمن أن يرى المستخدم دائماً شيئاً متسقاً: مؤشر انتظار، أو رسالة فشل، أو القائمة المتوقعة.

JSX
if (loading) return <p>Loading…</p>;
if (error) return <p>Error: {error}</p>;

return (
  <ul>
    {users.map((user) => (
      <li key={user.id}>{user.name}</li>
    ))}
  </ul>
);
العودة المبكّرة: حالة واحدة فقط تُعرَض في كل مرة.

فخّ أخير: إذا تغيّرت تبعية (مثل userId)، يخرج طلب جديد بينما قد يكون القديم لا يزال قيد التنفيذ. إذا أجاب القديم أخيراً، فسيطمس البيانات الحديثة. الإجراء المضاد الكلاسيكي: علَم ignore يُضبط إلى true بواسطة دالة التنظيف، للتخلص من الاستجابات القديمة.

JSX
useEffect(() => {
  let ignore = false;
  setLoading(true);

  fetch("/api/users/" + userId)
    .then((res) => res.json())
    .then((data) => {
      if (!ignore) setUser(data);
    })
    .finally(() => {
      if (!ignore) setLoading(false);
    });

  return () => {
    ignore = true;
  };
}, [userId]);
تُهمَل كل استجابة قديمة بفضل العلَم.

اختبار المعرفة

تأكّد من أنك تذكّرت النقاط الأساسية في هذا الدرس.

  1. لماذا نتتبع ثلاث حالات (بيانات، تحميل، خطأ) لطلب واحد؟
    • لأن fetch يتطلب هذه المتغيرات الثلاثة كي يعمل
    • كي تعكس الواجهة كل مرحلة: انتظار، أو فشل، أو نجاح
    • لمنع React من إعادة إطلاق الطلب عند كل عرض
  2. تستجيب واجهة API لديك بحالة 500، لكن الـ catch لا يُطلَق أبداً. لماذا؟
    • لا يرفض fetch الوعد إلا عند فشل الشبكة، لا عند حالة خطأ HTTP
    • لا يعمل catch داخل useEffect
    • يجب أن يرسل الخادم ترويسة خاصة لإطلاق الـ catch
  3. ما الغرض من العلَم ignore المضبوط إلى true في تنظيف التأثير؟
    • إلغاء طلب HTTP الجاري على جانب الخادم
    • منع React من إعادة تشغيل التأثير
    • إهمال استجابة طلب أصبح قديماً