توسعه فرانت‌اند

React Query: مدیریت Server State حرفه‌ای

Query Key، وضعیت دریافت، staleTime و gcTime، Invalidation و لغو با پروژه کامل React و پاسخ ساختگی مستقل برای فهم چرخه کش.

1404/11/18 3 دقیقه مطالعه 1,967 بازدید

دریافت داده سرور فقط یک Promise نیست. رابط باید بداند داده تازه است یا قدیمی، درخواست در حال انجام است یا شکست خورده و نتیجه قبلی را می‌توان هنگام دریافت دوباره نشان داد یا نه. TanStack Query که در بسیاری از متن‌ها React Query نامیده می‌شود، این چرخه را برای داده بیرونی مدیریت می‌کند. خود کتابخانه سرور، پایگاه داده یا جایگزین Client HTTP نیست؛ تابع دریافت داده را شما مشخص می‌کنید.

پیش از استفاده، Server State را از وضعیت محلی جدا کنید. بازبودن پنجره، متن ویرایش‌نشده فرم و نتیجه فهرست سفارش‌ها مسئولیت یکسان ندارند. TanStack Query برای نگهداری نسخه‌ای از داده بیرونی، کش و هماهنگی دریافت‌ها مفید است. این ابزار همه داده‌ها را خودکار معتبر، مجاز یا دائمی نمی‌کند. قرارداد API، دسترسی و شکل پاسخ همچنان به طراحی برنامه وابسته‌اند.

Query Key هویت داده است

Query Key فقط یک برچسب ظاهری نیست؛ کش با آن داده‌ها را جدا می‌کند. اگر نتیجه به شناسه، صفحه یا فیلتر وابسته است، آن ورودی باید در کلید منعکس شود. استفاده از یک کلید یکسان برای داده متفاوت می‌تواند نتیجه نامرتبط نمایش دهد. راهنمای رسمی Query Keys قرارداد آرایه‌ای و رابطه کلید با متغیرها را توضیح می‌دهد. کلید باید قابل توضیح و ثابت باشد.

برای نمونه، فهرست همه سفارش‌ها با فهرست دارای فیلتر وضعیت یک نتیجه نیست. نام منبع، فیلتر و صفحه را مطابق نیاز در کلید مدل کنید. مقدارهای موقت نامرتبط را وارد نکنید؛ تغییر آن‌ها می‌تواند کش‌های اضافی بسازد. داده حساس را نیز بی‌دلیل در کلید و ابزار اشکال‌زدایی قرار ندهید. یک قرارداد مرکزی نام‌گذاری برای تیم می‌تواند Invalidating و پیگیری داده را روشن‌تر کند.

تابع دریافت باید موفقیت و شکست روشن داشته باشد

Query Function باید داده را برگرداند یا خطا پرتاب کند. اگر خطا را می‌گیرید و مقدار خالی برمی‌گردانید، کتابخانه ممکن است همان را موفقیت بداند. پاسخ HTTP ناموفق در Fetch نیاز به بررسی ok دارد. مقایسه Fetch و Axios این تفاوت را توضیح می‌دهد. TanStack Query می‌تواند با هرکدام استفاده شود؛ کش و Client HTTP دو لایه متفاوت‌اند.

داده دریافت‌شده را پیش از مصرف مطابق قرارداد بررسی کنید. TypeScript فقط با نوع اعلام‌شده، صحت JSON زمان اجرا را ثابت نمی‌کند. اگر API داده غیرمنتظره بدهد، باید تصمیم بگیرید خطا چگونه گزارش و نمایش داده شود. استفاده از Schema می‌تواند در این مرز مفید باشد؛ راهنمای Zod به اعتبارسنجی می‌پردازد. کتابخانه کش مسئول تشخیص معنای تجاری هر فیلد نیست.

نمونه کامل برای دیدن Cache و Refetch

سه فایل package.json، index.html و main.jsx زیر را در یک پوشه تازه بگذارید. Node نسخه ۲۲.۱۲ به بالا و npm لازم است. npm install و سپس npm run dev را اجرا کنید و نشانی Vite را باز کنید. این نمونه برای مستقل‌بودن، پاسخ یک سرویس را با Promise و داده ساختگی شبیه‌سازی می‌کند؛ هیچ API واقعی یا عملیات ذخیره در سرور ندارد. هدف، دیدن وضعیت و چرخه دریافت است.

فایل package.json

{"type":"module","scripts":{"dev":"vite"},"dependencies":{"react":"^19.0.0","react-dom":"^19.0.0","@tanstack/react-query":"^5.0.0"},"devDependencies":{"vite":"^8.0.0"}}

فایل index.html

<!doctype html>
<html lang="fa" dir="rtl">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>نمونه React</title></head>
<body><div id="root"></div><script type="module" src="/main.jsx"></script></body>
</html>

فایل main.jsx

import React from 'react';
import { createRoot } from 'react-dom/client';
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';
const queryClient = new QueryClient();
let round = 0;
async function readDemoItems() {
  await new Promise(resolve => setTimeout(resolve, 500));
  round += 1;
  return { round, items: [{ id: 1, title: 'مورد اول' }, { id: 2, title: 'مورد دوم' }] };
}
function App() {
  const query = useQuery({ queryKey: ['demo-items'], queryFn: readDemoItems, staleTime: 30000 });
  if (query.isPending) return <p role="status">دریافت اولیه...</p>;
  if (query.isError) return <p role="alert">داده نمایشی دریافت نشد.</p>;
  return <main><h1>داده ساختگی برای آموزش کش</h1>
    <p>نوبت دریافت: {query.data.round}</p>
    <ul>{query.data.items.map(item => <li key={item.id}>{item.title}</li>)}</ul>
    <p role="status">{query.isFetching ? 'در حال به‌روزرسانی...' : 'دریافت تمام شد.'}</p>
    <button type="button" disabled={query.isFetching} onClick={() => query.refetch()}>بازخوانی</button>
  </main>;
}
createRoot(document.getElementById('root')).render(
  <QueryClientProvider client={queryClient}><App /></QueryClientProvider>
);

QueryClient بیرون کامپوننت ساخته شده تا با هر Render کش تازه نسازد. Provider آن را به درخت می‌دهد. کلید demo-items هویت داده است و دکمه بازخوانی از refetch استفاده می‌کند. در دریافت مجدد، داده قبلی حفظ می‌شود و پیام جدا وضعیت Fetch را نشان می‌دهد. شمار نوبت فقط داده نمایشی است و نباید آن را نسخه واقعی سرور معرفی کرد. این مثال تعمداً Mutation و احراز هویت ندارد.

Pending و Fetching را جدا بخوانید

وضعیت اولیه بدون داده با دریافت دوباره هنگام وجود داده یکسان نیست. اگر در هر Fetch کل صفحه را با Spinner جایگزین کنید، کاربر ممکن است محتوای قابل استفاده را از دست بدهد. داده قبلی و نشانگر به‌روزرسانی می‌توانند هم‌زمان دیده شوند. خطا نیز ممکن است پس از داشتن داده رخ دهد؛ طراحی باید درباره نمایش داده قدیمی و پیام خطا تصمیم بگیرد. یک شرط ساده برای همه حالت‌ها کافی نیست.

در مثال، isPending برای شروع بدون داده و isFetching برای علامت به‌روزرسانی استفاده شده است. این نام‌ها را با نسخه‌های قدیمی آموزش قاطی نکنید. نمای کلی مستندات رسمی React Query مسیر جاری یادگیری را می‌دهد. اگر از نسخه دیگری استفاده می‌کنید، قرارداد همان نسخه را بخوانید. تغییر نام وضعیت‌ها بدون فهم رفتار می‌تواند پیام اشتباه بسازد.

staleTime با gcTime فرق دارد

staleTime درباره بازه تازه‌دانستن داده است؛ gcTime درباره ماندن Query غیرفعال در کش پیش از جمع‌آوری آن. تنظیم یکی جای دیگری را نمی‌گیرد. داده Stale لزوماً پاک نشده است و داده در کش لزوماً تازه نیست. راهنمای Important Defaults رفتار پیش‌فرض و Triggerهای دریافت دوباره را توضیح می‌دهد. این تنظیم‌ها را با عمر نشست یا ذخیره دائمی اشتباه نگیرید.

عدد مناسب به نوع داده وابسته است. فهرست تقریباً ثابت و وضعیت حساس به زمان نیاز یکسانی ندارند. در نمونه staleTime سی ثانیه فقط انتخاب آموزشی است، نه توصیه عمومی محصول. هنگام تمرکز دوباره پنجره یا بازگشت اتصال، رفتار دریافت می‌تواند طبق تنظیم‌ها تغییر کند. اگر آن را نمی‌شناسید، درخواست اضافی ظاهراً عجیب می‌شود. ابتدا قرارداد پیش‌فرض را بفهمید، سپس متناسب تغییر دهید.

Mutation و اعتبار کش

Mutation برای عملیات تغییر داده به کار می‌رود، اما موفقیت آن خودکار به معنی تازه‌شدن همه Queryهای مرتبط نیست. بعد از ذخیره، می‌توانید داده معلوم را در کش قرار دهید یا Queryهای مرتبط را Invalidate کنید. انتخاب به پاسخ سرور و رابطه داده‌ها وابسته است. راهنمای Invalidation این علامت‌گذاری و دریافت مرتبط را توضیح می‌دهد. Invalidating همه کش پس از هر تغییر معمولاً تصمیم بیش از حد گسترده‌ای است.

Optimistic Update می‌تواند پاسخ سریع‌تری نشان دهد، ولی به بازگشت در خطا و هماهنگی با نتیجه سرور نیاز دارد. برای عملیات حساس، نمایش موفقیت پیش از تأیید واقعی باید با دقت طراحی شود. نمونه بالا فقط خواندن را نشان می‌دهد و الگوی کامل ثبت سفارش نیست. اضافه‌کردن دکمه ذخیره بدون Server و قرارداد خطا، آموزش Mutation کامل نمی‌سازد. مرز نمونه باید روشن بماند.

لغو و درخواست‌های وابسته

تابع دریافت می‌تواند Signal ارائه‌شده توسط کتابخانه را به Client HTTP بدهد. اگر Signal مصرف نشود، رفتار لغو شبکه با فرض شما یکسان نخواهد بود. راهنمای Cancellation این رابطه را توضیح می‌دهد. نمونه Promise محلی بالا از شبکه استفاده نمی‌کند و برای نمایش لغو نوشته نشده است. نباید به آن ادعای توقف درخواست سرور اضافه کرد.

برای Query وابسته به شناسه، شرایط فعال‌شدن را روشن کنید تا درخواست با ورودی نامعتبر ارسال نشود. زنجیره طولانی دریافت‌ها می‌تواند تجربه را کند کند؛ گاهی API مناسب‌تر یا موازی‌سازی لازم است. کتابخانه وابستگی واقعی داده را حذف نمی‌کند. اگر دو درخواست مستقل‌اند، می‌توان درباره اجرای هم‌زمان تصمیم گرفت. اگر یکی نتیجه دیگری را لازم دارد، ترتیب باید در معماری منعکس باشد.

قرارداد خطا و Retry

تعداد تلاش دوباره را با نوع خطا هماهنگ کنید. نبود مجوز یا ورودی نامعتبر معمولاً با تکرار همان درخواست حل نمی‌شود. خطای موقت شبکه موقعیت دیگری دارد. برای Mutation حساس نیز تکرار می‌تواند اثر دوباره ایجاد کند و باید با قرارداد سرور بررسی شود. تنظیم پیش‌فرض را کورکورانه برای همه کارها مناسب ندانید. پیام قابل نمایش و جزئیات ثبت فنی را جدا نگه دارید تا اطلاعات خصوصی وارد رابط یا گزارش عمومی نشوند.

SSR، کاربر و طول عمر کش

در برنامه سمت سرور، QueryClient نباید به شکل نامناسب داده کاربران را میان درخواست‌ها مشترک کند. ساخت، Dehydrate و Hydrate باید با چارچوب و محدوده درخواست هماهنگ باشند. مثال کلاینتی این مقاله نسخه SSR نیست. هنگام خروج از حساب یا تغییر کاربر، درباره کش داده خصوصی تصمیم بگیرید. کلید متفاوت به تنهایی جای سیاست پاک‌سازی و سطح دسترسی را نمی‌گیرد.

برای انتخاب، ابتدا نوع داده، هویت Query، مرز تازگی و رفتار خطا را بنویسید. سپس تنظیم‌های لازم را کم و روشن نگه دارید. راهنمای انتخاب State Management کمک می‌کند وضعیت سرور را از وضعیت محلی جدا کنید. TanStack Query وقتی مفید است که قرارداد داده و رابط روشن باشد؛ هدف، کم‌کردن منطق تکراری و قابل فهم نگه‌داشتن چرخه داده است، نه پنهان‌کردن آن پشت تنظیمات نامعلوم.

واکنش به این مقاله

اشتراک‌گذاری این مقاله:
دیدگاه‌ها 0
۰ / ۲۰۰۰

هنوز دیدگاهی ثبت نشده

اولین نفری باشید که نظر می‌دهد!