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

⚛️ شرح React

جلب البيانات في React

الدرس 10 من 32· ⏱ 3 دقائق قراءة· 🗓 آخر تحديث: ١٧ يوليو ٢٠٢٦

النمط الأساسي: useState + useEffect + fetch

ثلاث حالات نحتاجها دائمًا: البيانات، التحميل، الخطأ.

import { useState, useEffect } from "react";
import {
  HttpClient, HttpUserRepository, UserRepository, UserDTO,
  AsyncState, useAsyncService, ApiError, NetworkError,
} from "./lib/react-base-repository";

// 1. Repository — الطبقة الوحيدة التي تعرف fetch
// لا يُسمح باستدعاء fetch/http مباشرة من المكوّنات
const http = new HttpClient("https://api.example.com");
const userRepo: UserRepository = new HttpUserRepository(http);

// 2. Service Hook — يغلّف Repository ويعرض حالة المكوّن
function useUsers(): AsyncState<UserDTO[]> & { refetch: () => void } {
  const result = useAsyncService(() => userRepo.getUsers());
  useEffect(() => { result.refetch(); }, [result.refetch]);
  return result;
}

// 3. المكوّن — لا يعرف fetch ولا axios ولا Repository
// يتعامل فقط مع hooks و domain exceptions
function Users() {
  const { status, data: users, error, refetch } = useUsers();

  if (status === 'loading') return <p>جارٍ التحميل...</p>;

  if (status === 'error') {
    const message = error instanceof ApiError
      ? error.message
      : error instanceof NetworkError
        ? 'تعذّر الاتصال — تحقق من اتصالك بالإنترنت'
        : 'حدث خطأ غير متوقع';
    return (
      <div role="alert">
        <p>خطأ: {message}</p>
        <button onClick={refetch}>إعادة المحاولة</button>
      </div>
    );
  }

  return (
    <ul>
      {users.map((u) => (
        <li key={u.id}>{u.name}</li>
      ))}
    </ul>
  );
}

بـ async/await (أنظف)

لا يمكن جعل دالة useEffect نفسها async، فنعرّف دالة داخلية:

import { HttpClient, ApiError, NetworkError } from "./lib/react-base-repository";

// ❌ قبل Repository — fetch مباشرة في المكوّن
// ✅ بعد Repository — المكوّن يستخدم hook فقط
function useUsers_V2(): AsyncState<UserDTO[]> & { refetch: () => void } {
  const result = useAsyncService(async () => {
    const response = await fetch("/api/users");
    if (!response.ok) throw ApiError.fromResponse(response);
    return response.json();
  });
  useEffect(() => { result.refetch(); }, [result.refetch]);
  return result;
}

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

import { useAsyncService } from "./lib/react-base-repository";

// عند تغيّر userId، إعادة جلب البيانات عبر Repository
function useUser(userId: string): AsyncState<UserDTO> & { refetch: () => void } {
  const result = useAsyncService(() => userRepo.getUser(userId));
  useEffect(() => { result.refetch(); }, [userId, result.refetch]);
  return result;
}

تجنّب تحديث مكوّن مُزال (Cleanup)

import { useAsyncService } from "./lib/react-base-repository";

// useAsyncService يعالج cleanup تلقائياً — لا حاجة لـ active flag
function useSafeData(): AsyncState<DataDTO> & { refetch: () => void } {
  const result = useAsyncService(() => http.get<DataDTO>("/data"));
  useEffect(() => { result.refetch(); }, [result.refetch]);
  return result;
}

ملاحظة: المكتبات الجاهزة

للمشاريع الكبيرة، مكتبات مثل React Query (TanStack Query) تتولّى التحميل والتخزين المؤقّت وإعادة المحاولة تلقائيًا — لكن فهم النمط اليدوي أساس مهم.

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

  • ❌ جعل دالة useEffect نفسها async → عرّف دالة داخلية بدلًا منها.
  • ❌ نسيان حالتي التحميل والخطأ → تجربة سيئة.
  • ❌ نسيان مفتاح key عند عرض القائمة.
  • ❌ تحديث الحالة بعد إزالة المكوّن → استخدم علم التنظيف.

خلاصة

اجلب البيانات بنمط useState (data/loading/error) + useEffect + fetch. اعرض حالة التحميل والخطأ، وأعد الجلب عبر الاعتماديات، ونظّف لتجنّب تحديث مكوّن مُزال. هذا أكثر أنماط React استخدامًا.

شرح جلب البيانات في React — React بالعربي
جلب البيانات في ReactReact بالعربي · The Code Fix

📚 لمزيد من التعمّق في React، راجِع التوثيق الرسمي لـ React.

هل كان هذا الدرس مفيدًا؟