React Query وإدارة حالة الخادم
المشكلة: حالة الخادم ≠ حالة العميل
- بيانات API تتغير من الخارج — التخزين المؤقت المحلي لا يكفي
- الحاجة للتزامن: إعادة جلب، تخزين مؤقت، تحديثات متفائلة
useEffect+fetchيزيد التعقيد: تحميل، خطأ، إلغاء، إعادة جلب
تركيب React Query
npm install @tanstack/react-query
الإعداد الأساسي
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // 5 دقائق قبل اعتبار البيانات قديمة
retry: (count, error) => error?.status !== 404 && count < 2,
},
},
});
function App() {
return (
<QueryClientProvider client={queryClient}>
<Users />
</QueryClientProvider>
);
}
Query Key Factory
import { queryOptions } from "@tanstack/react-query";
import { HttpClient, UserDTO } from "./lib/react-base-repository";
const http = new HttpClient("/api");
export const userKeys = {
all: ["users"] as const,
lists: () => [...userKeys.all, "list"] as const,
list: (filters?: Record<string, string>) => [...userKeys.lists(), filters] as const,
details: () => [...userKeys.all, "detail"] as const,
detail: (id: string) => [...userKeys.details(), id] as const,
};
export const usersQueryOptions = () =>
queryOptions({
queryKey: userKeys.lists(),
queryFn: () => http.get<UserDTO[]>("/users"),
});
Usage in Component
import { useQuery } from "@tanstack/react-query";
import { usersQueryOptions } from "./queries/users";
function Users() {
const { data, isLoading, error } = useQuery(usersQueryOptions());
if (isLoading) return <p>تحميل...</p>;
if (error) return <p role="alert">{error.message}</p>;
return <ul>{data?.map((u) => <li key={u.id}>{u.name}</li>)}</ul>;
}
Mutations
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { userKeys } from "./queries/users";
function CreateUserForm() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: (data: Omit<UserDTO, "id">) => http.post<UserDTO>("/users", data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: userKeys.lists() });
},
});
return <button onClick={() => mutation.mutate({ name: "جديد" })} disabled={mutation.isPending}>إضافة</button>;
}
الملخص
- React Query يدير حالة الخادم — ليس بديلاً عن Context أو Redux
queryOptionsتعرّف key و fetcher في مكان واحدuseMutationللكتابة +invalidateQueriesللتحديث- يلغي الحاجة لـ
useEffectفي جلب البيانات