چطوری مثل حرفهایها در ریاکت از API دیتا بگیریم؟

چطوری مثل حرفهایها در ریاکت از API دیتا بگیریم؟ با کمک React Query!
من عاشق سبک «اعلانی» (Declarative) هستم. شاید همین باعث شد عاشق React بشم. اینکه رابط کاربری رو مثل تابعی از state و props ببینی، کلاً دیدم رو نسبت به توسعهی فرانتاند عوض کرد.
قبل از React، تو دنیای «دستوری» (Imperative) اندروید غرق بودم و فکر میکردم اوضاع همینه — یهکم شلوغ، یهکم دردناک، ولی باید باهاش کنار بیای، درست مثل دردسرای روزمرهی زندگی.
ولی React؟ React یه جور مکاشفه بود. آروم تو گوشم گفت: «یه راه بهتر هم هست»، و یهو همهچی برام روشن شد.
برنامهنویسی اعلانی نهتنها راحتتر بود، بلکه حس آزادی میداد.
اما وقتی پای فراخوانی API وسط میاومد، فکر میکردم راهش فقط همون روش دستوریه — همینیه که هست دیگه.
تا اینکه به React Query برخوردم — یه کتابخونه که نهفقط کار با API رو آسون میکنه، بلکه کلی از اون کدهای تکراری و خستهکنندهای که همیشه مجبور بودیم بنویسیم رو حذف میکنه.
اگه تا حالا با کتابخونههایی مثل react-hook-form
کار کرده باشی و دیدی چطوری هندل کردن فرمها رو خیلی راحت میکنه، React Query هم دقیقاً همون حس رو برای گرفتن دیتا از API بهت میده.
یه جور میانبر مخفی که نمیدونستی دنبالش بودی، ولی وقتی پیداش میکنی، دیگه نمیتونی بدونش کار کنی.
React Query چیه؟
React Query بر پایهی دو مفهوم اصلی ساخته شده:
- Queries
- Mutation
Queryها در واقع همون درخواستهای "GET" هستن — یعنی وقتی میخوای از سرور اطلاعاتی بگیری. مثلاً لیست همهی فیلمها رو بیاری یا فقط فیلمهای مورد علاقهت رو لود کنی. این نوع عملیاتها رو میتونیم بهشکل اعلانی (Declarative) پیادهسازی کنیم (که جلوتر میگیم چطور).
Mutationها همونطور که از اسمشون مشخصه، شامل درخواستهایی مثل POST، PUT، DELETE و غیره هستن که باعث تغییر وضعیت سرور میشن. مثلاً وقتی یه فیلم جدید اضافه میکنی، یکی رو حذف میکنی یا یه فیلم رو به لیست علاقهمندیهات اضافه میکنی.
گرچه Mutationها رو نمیشه مثل Queryها کاملاً اعلانی نوشت، ولی React Query (همونطور که تو مقالههای بعدی خواهیم دید) کلی از کدهای اضافی و تکراریشون رو حذف میکنه.
توی این مقاله، تمرکز ما فقط روی Queryها توی React Query هست.
توجه:
هر جا توی این مقاله از "React Query" حرف میزنم، منظورم دقیقاً پکیج رسمی و پشتیبانیشدهی @tanstack/react-query هست؛ یعنی نسخهای از این کتابخونه که تحت سازمان TanStack توسعه داده میشه و بهروز نگه داشته شده.
Queryها: فراخوانی داده بهصورت اعلانی
فرض کن یه API داری به نام fetchMovies
که لیست فیلمها رو برمیگردونه. توی React Query، فراخوانی دادهها بهصورت اعلانی انجام میشه و برای این کار از هوک useQuery استفاده میکنیم. اینجوری میتونی ازش برای فراخوانی فیلمها استفاده کنی:
import { fetchMovies } from '@/api';
const { data } = useQuery({
queryKey: ['movies'],
queryFn: fetchMovies,
});
بیایم جزئیتر نگاه کنیم:
· queryFn:
تابعی که واقعاً درخواست API رو انجام میده. یعنی همون جایی که اطلاعات رو از سرور میگیریم.
· queryKey:
یه شناسهی یکتاست برای این query و دیتایی که برمیگردونه. هر وقت این کلید تغییر کنه، React Query خودش بهصورت خودکار یه بار دیگه درخواست رو میفرسته تا دادهی جدید بگیره.
جدا از این، React Query اون دادهای که قبلاً گرفته رو با استفاده از همین queryKey کش میکنه. یعنی اگه دوباره همون query اجرا بشه، بهجای اینکه دوباره از سرور بپرسه، مستقیماً از کش (Cache) برمیداره — مگر اینکه نیاز به دادهی تازه باشه.
این کش کردن شبیه عملکرد useMemo تو ریاکت هست، ولی در مقیاسی بزرگتر و در سطح کل برنامه.
مدیریت حالتهای بارگذاری (Loading) و خطا (Error)
تو یه برنامهی معمولی ریاکتی، مدیریت وضعیتهای لودینگ و ارور نیاز به کلی کد تکراری داره.
اما React Query این امکانات رو بهصورت پیشفرض و آماده در اختیارت میذاره — بدون اینکه خودت بخوای از صفر بنویسیشون.
const { data, error, isLoading } = useQuery({
queryKey: ['movies'],
queryFn: fetchMovies,
});
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return <MovieList movies={data}/>
React Query خودش این وضعیت ها رو پیگیری میکنه یعنی دیگه لازم نیست خودت دستی کلی فلگ مثل isFetching
یا hasError
تعریف و مدیریت کنی.
همهچی آماده و اتوماتیکه.
ارسال پارامتر به Queryها
حالا اگه APIت پارامتر بخواد چی؟ مثلاً بخوای فقط فیلمهای یه ژانر خاص رو بگیری؟
خیلی راحت میتونی این پارامترها رو از طریق queryKey
به هوک بفرستی:
const [genre, setGenre] = useState('action');
const { data } = useQuery({
queryKey: ['movies', genre],
queryFn: ({ queryKey }) => {
const [_key, genre] = queryKey;
return fetchMovies(genre);
},
});
React Query بهصورت خودکار queryKey
رو به تابعی که تو queryFn
تعریف کردی میفرسته.
چرا باید آرگومانها رو تو queryKey
بذاریم؟
وقتی همهی پارامترها رو تو queryKey
وارد میکنی، React Query میتونه خودش بهصورت خودکار تشخیص بده که چه زمانی باید دوباره دیتا رو بگیره (refetch کنه).
مثلاً اگه پارامترهای API عوض بشن، React Query میفهمه و دوباره درخواست میفرسته.
از نظر من، اینجاست که React Query واقعاً اعلانی (Declarative) میشه.
همونطور که رابط کاربریت بر اساس state ساخته میشه، اینجا هم فراخوانی APIها مثل یه تابع از state رفتار میکنن.
گرفتن دوبارهی داده (Refetch)
با اینکه ما عاشق سبک اعلانی هستیم، بعضی وقتها لازمه راه دستوری (Imperative) رو انتخاب کنیم — مثلاً وقتی بخوای دستی اطلاعات رو دوباره بگیری.
React Query برای این حالت هم راه حل داره و با استفاده از تابع refetch
میتونی خیلی راحت این کار رو انجام بدی:
const { data, refetch } = useQuery({
queryKey: ['movies', genre],
queryFn: fetchMovies,
});
<button onClick={refetch}>Refetch Movies</button>
مکانیزم تلاش مجدد
React Query بهصورت پیشفرض یه مکانیزم تلاش مجدد (retry) داره.
یعنی اگه دیدی چند بار درخواست شبکه با خطا مواجه شد، نگران نباش!
بهطور پیشفرض، React Query یه درخواست ناموفق رو تا ۳ بار بیسروصدا دوباره امتحان میکنه، و اگه باز هم موفق نشد، دیگه رهاش میکنه.
اینجا میتونی ببینی که چطور میتونی این رفتار رو به دلخواه خودت تنظیم کنی:
const { data, error } = useQuery( { queryKey : ['movies']
, queryFn: fetchMovies
, retry: 2
, retryDelay: 1000
});
به این روش، کنترل کامل روی اینکه React Query چطور با درخواستهای ناموفق برخورد کنه خواهی داشت.
میتونی از retry ساده فراتر بری و قابلیتهایی مثل backoff نمایی (exponential backoff) یا retry شرطی بر اساس نوع خطا رو پیادهسازی کنی.
مستند کامل retry رو میتونی اینجا ببینی.
نکته:
برای اینکه این رفتار retry تو کل برنامه تغییر کنه، میتونی از queryClient
استفاده کنی — که پایینتر بیشتر در موردش توضیح میدیم.
مدیریت کش (Cache Management) و باطلسازی (Invalidation)
دو مشکل اساسی در علوم کامپیوتر وجود داره: باطل کردن کش و انتخاب اسم برای متغیرها.
— Phil Karlton
React Query بهصورت پیشفرض، تازگی دادهها و کش رو با استفاده از دو مفهوم اصلی مدیریت میکنه:
staleTime
مشخص میکنه که دادهای که از API گرفته شده تا چه مدت "تازه" به حساب میاد.
تا وقتی که داده تازه باشه، React Query اون رو از کش میخونه و دیگه درخواست جدید نمیفرسته.
بهطور پیشفرض، staleTime
روی صفر میلیثانیه تنظیم شده، یعنی داده بلافاصله بعد از fetch شدن، کهنه محسوب میشه.
React Query در شرایط خاص مثل mount شدن مجدد کامپوننت، فوکوس شدن دوبارهی پنجره یا وصل شدن مجدد اینترنت، سعی میکنه دوباره داده رو بگیره (refetch).
gcTime (قبلاً با اسم cacheTime شناخته میشد)
مشخص میکنه که دادههای کششده بعد از اینکه آخرین کامپوننت استفادهکننده از اون داده unmount شد، تا چه مدت تو حافظه بمونه.
مقدار پیشفرضش ۵ دقیقهست.
اگه تا اون موقع هیچکس از اون داده استفاده نکنه، React Query اون رو از کش حذف میکنه (garbage collect).
برای باطل کردن کش بهصورت دستی، میتونی از queryClient
استفاده کنی.
ساختارش اینطوریه:
const queryClient = useQueryClient();
queryClient.invalidateQueries({ queryKey: ['movies']});
این دستور باعث میشه React Query دادههای مربوط به queryهایی که با اون queryKey
همخوانی دارن رو دوباره دریافت کنه (refetch)،
حتی اگه اون داده هنوز منقضی نشده باشه یا به عنوان "کهنه" علامت نخورده باشه.
queryClient چیه؟
queryClient
قلب اصلی React Query محسوب میشه که همهی فرآیندهای مربوط به گرفتن داده (data fetching)، کش کردن، همگامسازی (sync)، و... رو در کل برنامه مدیریت میکنه.
اگه بخوای رفتار React Query رو در سطح کل برنامه شخصیسازی کنی، این همون جاییه که باید سراغش بری.
راستی، باید اپلیکیشنت رو در سطح بالا (معمولاً در کامپوننت اصلی برنامه) با QueryClientProvider
بپیچی و queryClient
رو بهش بدی.
اینطوری React Query میتونه دادهها رو در سراسر برنامه مدیریت کنه.
یه نمونه سادهش رو اینجا میبینی:
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<YourApp />
</QueryClientProvider>
);
}
اگر علاقهمند به پیکربندی رفتار React Query هستید (برای مثال تنظیم stale time
، fetch on focus
و غیره)، میتونید مستندات رسمی رو برای یادگیری بیشتر بررسی کنید.
مثال باطلسازی کش: فیلمها، کشور، ژانر
فرض کنید که queryKey
شما از چند بخش تشکیل شده، مثلاً [movies, country, genre]
.
حالا، اگه بخواید فقط فیلمهای مربوط به کشور آمریکا رو باطل کنید، میتونید از کد زیر استفاده کنید:
// Invalidate cache for movies from a specific country
queryClient.invalidateQueries({queryKey: ['movies', 'US']});
این دستور باعث میشه که درخواستهای مرتبط با ترکیب movies
و US
دوباره ارسال بشه (re-fetch)،
ولی درخواستهای مربوط به ترکیبهای دیگه از کشور و ژانر باطل نخواهند شد.
اگر یک queryKey
عمومیتر مثل [movies]
رو باطل کنید، همهی درخواستهایی که با movies
شروع میشن (فارغ از اینکه به کدوم کشور یا ژانر مربوط هستن) باطل خواهند شد.
این رفتار به شما این امکان رو میده که کش رو بسته به نیاز اپلیکیشن ، به طور خاص یا عمومی باطل کنید.
// Invalidate all movies cache, regardless of country or genre
queryClient.invalidateQueries({queryKey: ['movies']});
React Query این سناریوها را بهطور هوشمندانه مدیریت میکند، بنابراین نیازی نیست نگران درخواستهای اضافی (refetching) یا مدیریت دستی کش باشید.
روشهای زیادی برای باطلسازی کش بسته به نیاز شما وجود دارد.
این چیزی بود که میخواستم به اشتراک بگذارم که چطور مفهوم declarative حتی به درخواستهای API هم گسترش پیدا میکند.
اما این فقط نوک کوه یخ است. React Query امکانات بسیار بیشتری ارائه میدهد، مانند mutations، pagination، infinite scroll و غیره.
واقعا پیشنهاد میکنم این ویژگیها را هم بررسی کنید.
React Query فوقالعاده قابل تنظیم است و از تجربهی خودم میگویم که تقریباً هر نیازی که در طول پروژهها خواهید داشت را پوشش میدهد.
مستندات عالی است و منابع زیادی برای کمک در دسترس است.
پس، اگر هنوز از روشهای قدیمی و imperative برای درخواستهای API استفاده میکنید، حتما وقتش رسیده که به React Query مهاجرت کنید.
از اینکه مقاله را خواندید متشکرم! امیدوارم چیز جدیدی یاد گرفته باشید.
دریافت مشاوره خرید
به مشاوره نیاز دارید؟ شماره تماس خود را بگذارید.دوره پیشنهادی

.png&w=256&q=75)
آموزش صفر تا صد javascript پروژه محور
دیدگاه کاربران
(0 دیدگاه)