الگوهای RSC که کار رو روی سرور نگه میدارن
خلاصهٔ کاملتر
تو App Router نکست، کامپوننتها بهصورت پیشفرض React Server Component هستن، ولی به گفتهٔ نویسنده تا یه قابلیت تعاملی وسط بیاد وسوسه میشیم کل کار رو بدیم دست کلاینت. آرورا شارف داره اپ اجتماعی کوچیکی به اسم Drop میسازه تا پیشنمایش Instant Navigations تو Next.js 16.3 رو تست کنه و تو این پست الگوهایی رو جمع کرده که نشون میدن مدل Server Component و Server Function تا کجا کش میآد.
استدلال پایه سادهست: اگه فید رو روی کلاینت بگیری، مرورگر باید اول کد کامپوننت رو دانلود کنه، یه اسپینر نشون بده و بعد تازه بره سراغ داده. سرور ولی کنار دیتابیس نشسته، پس یه async Server Component میتونه پستها رو بگیره و خروجی رندرشده تحویل بده. RSCها استریم هم میشن، یعنی بخشهای ثابت صفحه فوری سرو میشن و بخش دینامیک پشت Suspense جا میافته. Drop با cacheComponents اجرا میشه تا هرچی دینامیک نیست با Partial Prerendering پیشرندر و از CDN سرو بشه.
نسخهٔ بدیهی دکمهٔ «بارگذاری بیشتر» صفحهٔ بعدی رو روی کلاینت فچ میکنه و به یه لیست تو state اضافه میکنه — همون کاری که React Query و SWR حولش ساخته شدن. نویسنده چند ایراد براش میشمره: نیاز به یه روت /api/feed جدا، از بین رفتن فید با یه بار رفرش، URLـی که موقع اشتراکگذاری همیشه به صفحهٔ یک اشاره میکنه، و پستهایی که بیرون از سیستم کش نکست میمونن و با یه mutation بهروز نمیشن. تو Drop اصلاً پستها روی کلاینت رندر نمیشن، چون بلوکهای کد با Shiki و فقط روی سرور هایلایت میشن.
راهحل پیشنهادی اینه که دکمه فقط یه کلاینتکامپوننت خیلی کوچیک باشه که شمارهٔ صفحهٔ بعدی رو تو URL میذاره و برای وضعیت pending از یه transition استفاده میکنه — تنها کد کلاینتی که این قابلیت لازم داره:
'use client';
export function LoadMore({ href }: { href: Route }) {
const router = useRouter();
const [isPending, startTransition] = useTransition();
return (
<button disabled={isPending} onClick={() => {
startTransition(() => router.push(href, { scroll: false }));
}}>{isPending ? 'Loading…' : 'Load more'}</button>
);
}بعدش کامپوننت سروری Feed پارامتر page رو از URL میخونه و صفحههای ۱ تا N رو رندر میکنه، هر کدوم یه async Server Component داخل مرز Suspense خودش. چون هر صفحه مرز جداگونهٔ خودش رو داره، صفحهٔ تازه زیر یه اسکلتون استریم میشه و صفحههای قبلی دقیقاً سر جاشون میمونن. نویسنده همین منطق رو برای دو الگوی دیگه هم پیش میبره: فیلد جستوجویی که بخشی از پوستهٔ استاتیک سروره و فوکوسش رو موقع استریم شدن نتایج نگه میداره، و کامپوزر پیامی که پیشنمایش پیشنویس رو از یه Server Function میگیره.
نکات کلیدی:
- دکمهٔ بارگذاری بیشتر هیچ دادهای فچ نمیکنه؛ فقط شمارهٔ صفحه رو به URL پوش میکنه
- هر صفحه مرز Suspense خودش رو داره، پس صفحهٔ جدید استریم میشه بدون اینکه قبلیها تکون بخورن
- نگه داشتن فید تو state کلاینت یعنی از دست دادن اشتراکگذاری URL، کش نکست و بقای فید بعد از رفرش
- با cacheComponents بخش غیردینامیک صفحه با Partial Prerendering از CDN سرو میشه
- کل قابلیت فقط یه کلاینتکامپوننت کوچیک لازم داره؛ بقیهش سروریه




