Server Component جای TanStack Query را نگرفت
کامپوننت سرور در React مرز درخواست و HTML اول را مالک است. TanStack Query نسخه ۵ مالک چیزی است که بعد از تعاملی شدن همان HTML اتفاق میافتد. پولینگ، refetch با برگشت فوکوس، اسکرول بیانتها، بهروزرسانی خوشبینانه، و هر mutation که باید کش زنده را منسجم نگه دارد. اگر Query را حذف کردی چون «حالا RSC داریم»، ابزاری را برداشتهای که روی کلاینت زمان را میفهمد.
این انتخاب دوتایی نیست. داشبورد واقعی هر دو را میخواهد. اولین رنگ مسئله سرور است. بیست دقیقه بعدی نشست مسئله خط زمان مرورگر است. این دو شغل را یکی نکن.
کامپوننت سرور چه چیزی را واقعاً دارد
برای رندر اول روی سرور اجرا میشود و در ناوبریهایی که دوباره به همان درخت مسیر میخورند هم سرور در کار است. میتواند کوکی بخواند، به دیتابیس بزند، API داخلی را صدا بزند، و با Suspense خروجی را جریان بدهد. در App Router کنترل کش کنار همین مدل است: `revalidatePath`، `revalidateTag`، و کش قطعه مسیر تصمیم میگیرند درخواست بعدی درخت تازه ببیند یا نه.
برای صفحه محتوا، پوسته بازاریابی، و خواندنی که باید قبل از رنگ معتبر باشد عالی است. برای «این جدول تا وقتی تب باز است تازه بماند» ابزار بدی است. کامپوننت سرور در مرورگر نشسته تا رویداد فوکوس را بشنود. ردیف خوشبینانه را وسط درخواست داخل لیست قاطی نمیکند. وقتی کاربر از تب دیگر برمیگردد، بازتولید مسیر مربوط به انتشار هفته پیش هیچ کاری برای همین نشست نمیکند.
`router.refresh()` گاهی کافی است. برای یک ذخیره که باید همان صفحه سروری را دوباره بگیری. برای هماهنگی کش چند کلید، صفحهبندی مکاننما، و برگشت بعد از خطای mutation، زود تبدیل میشود به پیادهسازی نصف Query با اسمهای داخلی خودتان.
Query هنوز مالک ساعت کلاینت است
کلید، `staleTime`، بازه refetch، refetch با فوکوس پنجره، کوئری بیانتها، و `invalidateQueries` وجود دارند چون نشست مرورگر بعد از پاسخ اول هنوز تغییر میکند. سؤالهایی که RSC جواب نمیدهد: این کلید همین حالا کهنه است یا نه، چون پنجره فوکوس گرفته باید دوباره بگیرم یا نه، چطور صفحه بعدی فید را بدون پریدن اسکرول نگه دارم، و اگر mutation شکست خورد ردیف خوشبینانه را چطور برگردانم.
نمونههایی که حذف Query همان هفته درد میشود. تخته عملیاتی که تا تب دیده میشود هر چند ده ثانیه پول میکند. جدول مدیریت با فیلتر که با برگشت کاربر از اپ دیگر باید تازه شود. فید فعالیت بلند با صفحههای مکاننما که از قبل در کشاند. فرمی که ردیف تازه را قبل از تمام شدن شبکه نشان میدهد و در خطا برمیگرداند.
میشود چند تا از اینها را با EventSource خانگی و refresh مسیر تقلید کرد. معنی کش را بد از نو میسازی و بعد اسم دیگری برای باطلسازی پیدا میکنی. اگر محصول واقعاً فقط خواندن سروری است، Query را وارد نکن. اگر نشست زنده است، حذفش صرفهجویی نیست.
دستبهدست که هر دو را لازم میکند
الگوی همراه با App Router این است که روی سرور prefetch کنی، وضعیت را dehydrate کنی، و با `HydrationBoundary` به زیر درخت کلاینت بدهی. یک `QueryClient` برای همان درخواست، کلیدهایی که رنگ اول لازم دارد گرم میشوند، و `useQuery` یا `useSuspenseQuery` همان کلید را بدون فلش بارگذاری خالی میخواند.
دو جزئیات راهنمای SSR خود کتابخانه در عمل مهم است. اول، `staleTime` پیشفرض خیلی کوتاه باعث میشود کلاینت بلافاصله همان داده تازههیدراتشده را دوباره بگیرد. راهنما برای همین سناریو یک `staleTime` در حد حدود یک دقیقه را بهعنوان پیشفرض SSR مطرح میکند تا درخواست تکراری همان ثانیه اول را نکشی. این عدد قانون محصول نیست. اگر داده باید همیشه لحظهای باشد، کوتاهترش کن و هزینه را قبول کن. اگر محتوای کمتغییر است، بلندتر.
دوم، از نسخههای جدیدتر v5 میشود کوئری هنوز ناتمام را هم dehydrate کرد تا تعلیق سرور و کلاینت به هم نزدیک بمانند. اگر سند نسخهای که در ریپو قفل کردهای این را ندارد، رفتار را از روی حافظه وبلاگ فرض نکن. نسخه نصبشده را بخوان.
`QueryClient` را بین درخواستها روی سرور به اشتراک نگذار. کش یک کاربر نباید به رندر کاربر بعدی نشت کند. کلاینت مرورگر میتواند یک کلاینت پایدار داشته باشد. این دو عمر را قاطی نکن.
مرز را اینطور بنویس
خواندن عمومی و صفحهای که باید برای بازدید اول کامل باشد در کامپوننت سرور، با کشی که مالک باطلسازی دارد. هر چیزی که به ساعت تب، خوشبینی، یا صفحهبندی زنده بند است در Query. mutation با Server Action اشکالی ندارد؛ بعد از موفقیت، کلیدهای کلاینت را باطل کن یا کش را با پاسخ سرور همخوان کن. اگر فقط سرور را refresh کنی و کش کلاینت را ول کنی، UI دو حقیقت نشان میدهد.
Query را برای داده کماهمیت تزئینی نگذار تا «معماری یکی باشد». هر کلید یک قرارداد تازگی است. کلید بیصاحب یعنی refetch شانسی.
نشانهای که مرز را غلط چیدهای
اگر هر ذخیره، کل صفحه را چشمک میزند، احتمالاً فقط `router.refresh()` داری و حالت محلی جدول را دور میریزی. اگر برعکس، بعد از ویرایش سرور عدد تازه است ولی کارت کلاینت عدد قدیم را نشان میدهد، کلید Query را به نتیجه mutation وصل نکردهای. اگر شبکه در ثانیه اول دو بار همان فهرست را میگیرد، `staleTime` هیدرات با عمر داده نمیخواند.
این سه نشانه را در بازبینی PR بپرس. جوابشان مهمتر از این است که پوشه هوکها چقدر شلوغ است.
پرسشهای کوتاه
همه fetchها را ببرم داخل Query؟ نه. صفحه مقاله که با بازتولید برچسب تازه میشود به کش کلاینت نیاز ندارد. هزینه آبرسانی و جاوااسکریپت را بیدلیل میخري.
Query جای کش Next.js است؟ نه. یکی تازگی نشست مرورگر است، یکی تازگی پاسخ و مسیر بین درخواستها. هر دو را خاموش کردن یا یکی را جای دیگری گذاشتن باگ متفاوت میسازد.
نسخه ۴ را با همین حرف نگه دارم؟ الگوی ذهنی شبیه است، جزئیات هیدرات و API نه. اگر مهاجرت میکنی، prefetch و مرز کلید را تست کن نه فقط بالا آمدن صفحه.