مكوّنات React Server: غوص عملي
كيف تعمل حدود RSC والبث والفصل بين العميل/الخادم في App Router لـ Next.js — ومتى تلجأ لمكوّن عميل.
تمثل مكوّنات React Server Components (RSC) أحد أهم التحولات المعمارية في منظومة React منذ ظهور الـ hooks. وعند دمجها مع Next.js App Router، يمكنك عرض المكوّنات على الخادم افتراضيًا، وإرسال المخرجات المُسلسلة فقط إلى العميل، وحجز JavaScript على جانب العميل للتفاعلات التي تحتاجها فعلًا. فهم موضع انتهاء مكوّنات الخادم وبداية مكوّنات العميل أمر أساسي لبناء تطبيقات سريعة وقابلة للصيانة في 2026.
ما هي React Server Components؟
مكوّنات React Server Components، ويُختصرها غالبًا RSC، هي مكوّنات تُنفَّذ حصريًا على الخادم أثناء الطلب أو وقت البناء. وعلى عكس العرض من جانب الخادم التقليدي الذي يُ hydrate الصفحة بالكامل على العميل، ترسل RSC تمثيلًا مُسلسلًا مضغوطًا يُسمى React Flight payload. يستقبل العميل هذه الحمولة ويعيد بناء واجهة المستخدم دون تنزيل كود المكوّن أو تبعياته.
يوفر هذا النموذج عدة فوائد عملية للتطبيقات الإنتاجية:
- تقليل حجم الحزمة — التبعيات الخاصة بالخادم لا تُرسَل أبدًا إلى المتصفح.
- وصول مباشر للبيانات — يمكن للمكوّنات الاستعلام من قواعد البيانات وأنظمة الملفات دون كشف بيانات الاعتماد عبر مسارات API.
- تقسيم تلقائي للكود — كل حدود مكوّن خادم تُنشئ نقطة تقسيم طبيعية.
- تحسين Time to First Byte — يتيح البث عرض الهيكل بينما تُحل البيانات الأبطأ.
النموذج الذهني بسيط: اعتبر مكوّنات الخادم طبقة العرض الافتراضية، ومكوّنات العميل جزر تفاعلية اختيارية موزعة في الشجرة.
فهم حدود RSC في Next.js
في Next.js App Router، كل ملف داخل مجلد app هو Server Component ما لم تُضِف التوجيه "use client" في أعلى الملف. يُنشئ هذا التوجيه حدًا للعميل. كل ما يُستورد في ذلك الملف يصبح جزءًا من حزمة العميل، ويمكن عرض الأبناء المُمرَّرين كـ props من الخادم عبر تقنية تُسمى composition.
التركيب عبر الحدود
من أقوى الأنماط تمرير مكوّنات الخادم كأبناء لمكوّنات العميل. يُعرض مكوّن الخادم أولًا، وتُبث مخرجاته إلى فتحة مكوّن العميل. هذا يتجنب الخطأ الشائع بجعل الصفحة بأكملها client component لمجرد أن قسمًا صغيرًا يحتاج state أو معالجات أحداث.
// ServerComponent.tsx (no directive — runs on server)
async function ProductList() {
const products = await db.query("SELECT * FROM products");
return (
<ul>
{products.map((p) => (
<li key={p.id}>{p.name}</li>
))}
</ul>
);
}
// ClientWrapper.tsx
"use client";
export function ClientWrapper({ children }: { children: React.ReactNode }) {
const [expanded, setExpanded] = useState(false);
return (
<div>
<button onClick={() => setExpanded(!expanded)}>Toggle</button>
{expanded && children}
</div>
);
}
// page.tsx (Server Component)
export default function Page() {
return (
<ClientWrapper>
<ProductList />
</ClientWrapper>
);
}
لاحظ أن ProductList لا يصبح client component رغم وجوده داخل غلاف تفاعلي. يعرضه الخادم، ويُسلسل الناتج، ويستقبل العميل الترميز الجاهز.
البث و Suspense
يستفيد Next.js من قدرات React للبث لإرسال HTML إلى المتصفح تدريجيًا. عند لف async server components داخل حدود <Suspense>، يمكن للإطار عرض واجهة احتياطية فورًا بينما تُحل الأقسام الأبطأ في الخلفية.
البث فعّال خاصة للصفحات ذات مصادر بيانات متباينة. قد تُحل طبقة cache سريعة خلال milliseconds بينما يستغرق aggregation query ثوانٍ. بدون بث، يرى المستخدم شاشة فارغة حتى يكتمل كل شيء. مع البث، تظهر شريط التنقل والتخطيط والمحتوى المخزّن فورًا.
- ضع حدود
<Suspense>حول مناطق جلب بيانات مستقلة. - استخدم واجهة احتياطية ذات معنى — skeleton loaders أفضل من spinners عامة.
- ضع حدودًا متداخلة للتحكم الدقيق في ما يُبث أولًا.
- ادمج مع ملفات
loading.tsxلواجهات احتياطية على مستوى المسار.
يجمع RSC والبث بينهما تغييرًا جذريًا في التفكير بأداء تحميل الصفحة. بدل تحسين تتابع طلبات واحد، تُصمّم الصفحات كسلسلة أقسام قابلة للبث بشكل مستقل.
متى تستخدم Client Components
رغم افتراضية الخادم، لا غنى عن client components. أي ميزة تعتمد على browser APIs أو local state أو effects أو event handlers تحتاج حد العميل. الهدف ليس إزالة client components بل تقليل بصمتها.
استخدم "use client" عند الحاجة إلى أي مما يلي:
- معالجات الأحداث — onClick و onChange و onSubmit وتفاعلات DOM المماثلة.
- React hooks — useState و useEffect و useReducer و useContext و custom hooks المعتمدة عليها.
- واجهات برمجية للمتصفح فقط — localStorage و geolocation و IntersectionObserver و Web Audio.
- مكتبات طرف ثالث — كثير من مكتبات الرسوم والخرائط والحركة تفترض بيئة متصفح.
قاعدة عملية: ادفع حد العميل لأسفل شجرة المكوّنات قدر الإمكان. بدل جعل dashboard كاملًا client component، استخرج chart أو filter panel تفاعليًا كجزيرة صغيرة، وأبقِ التخطيط والعناوين وجداول البيانات على الخادم.
أنماط جلب البيانات
يمكن لمكوّنات الخادم جلب البيانات مباشرة عبر async/await على مستوى المكوّن. لا حاجة لـ useEffect أو مكتبة جلب منفصلة للقراءات من الخادم. يوسّع Next.js ذلك بـ caching semantics عبر fetch و unstable_cache.
// Direct async fetch in a Server Component
async function BlogPost({ slug }: { slug: string }) {
const post = await fetch(`https://api.example.com/posts/${slug}`, {
next: { revalidate: 3600 },
}).then((res) => res.json());
return (
<article>
<h1>{post.title}</h1>
<p>{post.body}</p>
</article>
);
}
للتعديلات، تتكامل React 19 Actions طبيعيًا مع server components. تُعرّف server actions بتوجيه "use server" وتستدعيها من نماذج العميل دون بناء endpoints يدويًا. هذا يغلق الحلقة بين العرض على الخادم والتعديلات من جانب الخادم بأمان أنواع وموازاة في المكان.
أخطاء شائعة وأفضل الممارسات
فرق جديدة على RSC تواجه أخطاء متكررة. تمرير قيم غير قابلة للتسلسل مثل functions أو class instances من الخادم إلى client components يسبب أخطاء runtime. استيراد server-only modules في client components يفشل البناء. وجعل أشجار مكوّنات كبيرة client components يُبطل الهدف من المعمارية.
اتبع هذه الممارسات:
- اجعل server components افتراضية وأضف حدود العميل بجراحة دقيقة.
- ضع جلب البيانات بجانب المكوّنات التي تستهلكها.
- استخدم TypeScript لاكتشاف مشاكل التسلسل وقت الترجمة.
- حلّل client bundle بانتظام للتأكد أن server components تؤدي دورها.
- وثّق قرارات الحدود في codebase ليحافظ الفريق على الاتساق.
React Server Components ليست حلًا سحريًا، لكنها نموذج متماسك لبناء تطبيقات سريعة افتراضيًا. إتقان الحدود والبث ونمط composition بين الخادم والعميل يُثمر في كل مشروع تُطلقه عبر Next.js App Router.