شرح React Query (TanStack Query) من الصفر بالعربي
هذا الدرس هو شرح React Query (TanStack Query) من الصفر بالعربي على شكل React Query tutorial بالعربي. عندما تبدأ بجلب البيانات في React من API، ستواجه نفس التحديات دائمًا: تحميل، أخطاء، كاش، إعادة جلب. هنا يأتي دور React Query (المعروف الآن باسم TanStack Query) ليحل هذه المشاكل بشكل احترافي.
لماذا React Query لإدارة البيانات في React؟
- كاش ذكي للبيانات بدل تكرار نفس الطلب.
- حالات تحميل وأخطاء جاهزة بدون تعقيد.
- إعادة جلب تلقائية عند التركيز أو تغيير الشبكة.
- تحكم كامل في التحديث وإعادة المحاولة.
التثبيت والإعداد الأساسي لـ TanStack Query
npm install @tanstack/react-query
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<YourApp />
</QueryClientProvider>
);
}
شرح مفصل خطوة بخطوة للمبتدئ جدًا:
- استوردنا
QueryClientوQueryClientProvider. - أنشأنا
queryClientمرة واحدة فقط. - لفّينا التطبيق بـ
QueryClientProviderحتى تصل React Query لكل المكوّنات. - بعد ذلك تستطيع استخدام
useQueryفي أي مكان داخل التطبيق.
كأنك وضعت “مركز إدارة بيانات” واحد للتطبيق كله، بدل ما كل مكوّن يحاول يدير البيانات لوحده.
مثال عملي: useQuery لجلب البيانات في React
import { useQuery } from "@tanstack/react-query";
async function fetchUsers() {
const res = await fetch("https://jsonplaceholder.typicode.com/users");
if (!res.ok) throw new Error("فشل تحميل البيانات");
return res.json();
}
function Users() {
const { data, isLoading, error } = useQuery({
queryKey: ["users"],
queryFn: fetchUsers,
});
if (isLoading) return <p>جاري التحميل...</p>;
if (error) return <p className="text-danger">{error.message}</p>;
return (
<ul>
{data.map((u) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}
الشرح خطوة بخطوة بالتفصيل:
- أنشأنا دالة
fetchUsersوظيفتها الوحيدة جلب البيانات من الـ API. - داخل الدالة: نتحقق من
res.ok، إذا فشل الطلب نرمي خطأ. - إذا نجح الطلب نحول الرد إلى JSON ثم نعيده.
- في المكوّن
UsersاستدعيناuseQueryبدل استخدامuseEffectوuseStateيدويًا. - أعطينا الاستعلام اسمًا واضحًا عبر
queryKey: ["users"]لكي يدير الكاش بشكل صحيح. - القيمة
queryFnهي الدالة التي تجلب البيانات فعليًا. - عندما يبدأ أول تحميل،
isLoadingيصبحtrueونُظهر رسالة التحميل. - إذا حدث خطأ يظهر داخل
errorونُظهر رسالة مناسبة. - إذا نجح الطلب، تظهر البيانات في
dataونعرض القائمة.
لاحظ كيف اختفت معظم حالات إدارة الـ state يدويًا. React Query تتولى الكاش (React Query cache)، والتحكم في التحميل والأخطاء بشكل مباشر.
إعدادات مهمة: queryKey و staleTime و refetchOnWindowFocus
staleTime: مدة اعتبار البيانات جديدة قبل إعادة الجلب.refetchOnWindowFocus: إعادة الجلب عند العودة للصفحة.enabled: تشغيل أو إيقاف الاستعلام حسب شرط معيّن.
const { data } = useQuery({
queryKey: ["users"],
queryFn: fetchUsers,
staleTime: 1000 * 60, // دقيقة
refetchOnWindowFocus: false,
});
تفسير مفصل خطوة بخطوة:
queryKey: هو الاسم الفريد للاستعلام، ومنه React Query تعرف أي كاش تستخدم.queryFn: الدالة المسؤولة عن الجلب الفعلي للبيانات.staleTime: المدة التي تعتبر فيها البيانات جديدة، هنا دقيقة واحدة.refetchOnWindowFocus: عند العودة للتبويب لن يعيد الجلب تلقائيًا.
هذا يجعل إدارة البيانات في React أكثر هدوءًا وأقل طلبات غير ضرورية.
استخدام useMutation لإرسال البيانات (POST/PUT/DELETE)
import { useMutation, useQueryClient } from "@tanstack/react-query";
function AddUser() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: async (newUser) => {
const res = await fetch("https://jsonplaceholder.typicode.com/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(newUser),
});
if (!res.ok) throw new Error("فشل إضافة المستخدم");
return res.json();
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["users"] });
},
});
return (
<button onClick={() => mutation.mutate({ name: "Ali" })}>
إضافة مستخدم
</button>
);
}
شرح مفصل خطوة بخطوة:
useMutationمخصص للعمليات التي تغيّر البيانات (POST/PUT/DELETE).- داخل
mutationFnنرسل المستخدم الجديد إلى الـ API. - إذا فشل الطلب، نرمي خطأ حتى تتعامل React Query معه.
- عند النجاح نستخدم
onSuccessلتحديث البيانات المعروضة. invalidateQueriesتجبر استعلام["users"]على إعادة الجلب.
بهذه الطريقة تبقى الواجهة محدثة تلقائيًا — بدون تحديث الصفحة.
أفضل الممارسات المختصرة
- اجعل
queryKeyواضحًا ومعبّرًا مثل["users", id]. - استخدم
staleTimeلتقليل الطلبات غير الضرورية. - اعزل منطق الجلب في دوال مستقلة لسهولة الاختبار وإعادة الاستخدام.
Pagination و Infinite Query في React Query
في صفحات طويلة أو قوائم غير منتهية، ستحتاج إلى React Query pagination أو React Query infinite query. الفكرة أنك تجلب صفحات صغيرة وتضيفها تدريجيًا بدل تحميل كل شيء دفعة واحدة.
import { useInfiniteQuery } from "@tanstack/react-query";
function Posts() {
const { data, fetchNextPage, hasNextPage, isFetching } = useInfiniteQuery({
queryKey: ["posts"],
queryFn: ({ pageParam = 1 }) =>
fetch(`https://jsonplaceholder.typicode.com/posts?_page=${pageParam}&_limit=10`)
.then((res) => res.json()),
getNextPageParam: (lastPage, allPages) => allPages.length + 1,
});
return (
<div>
{data?.pages.flat().map((p) => (
<p key={p.id}>{p.title}</p>
))}
{hasNextPage && (
<button onClick={() => fetchNextPage()}>
{isFetching ? "جاري التحميل..." : "تحميل المزيد"}
</button>
)}
</div>
);
}
شرح خطوة بخطوة للمبتدئ جدًا:
- استعملنا
useInfiniteQueryبدلuseQueryلأنه يدعم الصفحات المتعددة. queryKeyيعطي هوية ثابتة للكاش.queryFnيستقبلpageParamليعرف أي صفحة يطلب.- كل ضغطة على “تحميل المزيد” تنفذ
fetchNextPage(). getNextPageParamيحدد رقم الصفحة التالية بناءً على عدد الصفحات الحالية.- النتيجة تُخزن في
data.pages، لذلك ندمجها بـflat().
هذا الأسلوب مثالي لـ React Query pagination و React Query infinite query.
React Query Devtools (لمراقبة الكاش)
أثناء التطوير يمكنك تفعيل React Query devtools لرؤية الكاش والاستعلامات الحالية.
هذا يساعدك على فهم سلوك queryKey و staleTime بسرعة.
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
function App() {
return (
<QueryClientProvider client={queryClient}>
<YourApp />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
شرح مفصل خطوة بخطوة:
- نستورد
ReactQueryDevtoolsمن الحزمة الخاصة بها. - نضيفها داخل
QueryClientProviderحتى ترى نفس الكاش. initialIsOpen={false}يعني أن اللوحة تبدأ مغلقة.
ببساطة: Devtools لوحة تعرض لك الكاش والاستعلامات. سترى queryKey الحالي، وهل البيانات stale أو fresh، وهذا يساعدك على فهم سلوك React Query بسرعة.
الأسئلة الشائعة — FAQ
ما هو React Query؟
مكتبة لإدارة جلب البيانات مع كاش وحالات تحميل وأخطاء جاهزة.
هل أحتاج React Query مع Fetch أو Axios؟
نعم، لأنها تضيف كاش وإعادة جلب وتحكم متقدم بالحالة.
ما الفرق بين isLoading و isFetching؟
isLoading لأول تحميل، وisFetching لإعادة الجلب في الخلفية.
متى أستخدم useMutation؟
عند إرسال بيانات أو تعديلها مثل POST/PUT/DELETE.
هل React Query مناسب للمشاريع الصغيرة؟
نعم إذا لديك بيانات مشتركة بين أكثر من شاشة.