Serialization و محدودیت‌های انتقال داده

در درس قبل دیدیم Server Component می‌تواند به یک Client Component، Prop یا children بدهد. اما این Propها همیشه مثل یک ارجاع ساده جاوااسکریپتی عمل نمی‌کنند. چون Server و Client دو محیط جدا هستند (روی دو ماشین متفاوت)، هر مقداری که از این مرز عبور می‌کند باید به یک قالب قابل انتقال تبدیل شود. به این فرایند Serialization می‌گویند و این درس به محدودیت‌های آن می‌پردازد.

چرا Serialization لازم است

در فصل‌های قبل، وقتی یک Prop را از والد به فرزند می‌دادیم، هر دو در یک حافظه و یک مرورگر بودند؛ پس می‌شد یک Object یا تابع را مستقیماً با ارجاع رد کرد (و همان‌طور که در فصل سیزدهم دیدیم، Referential Equality اهمیت داشت). اما وقتی Server Component یک Prop به Client Component می‌دهد، آن مقدار باید از شبکه عبور کند. چیزی که روی شبکه می‌رود، متن یا داده است، نه ارجاع به حافظه سرور. پس React مقدار را در سمت سرور به یک قالب انتقالی تبدیل می‌کند و در مرورگر دوباره می‌سازد.

چه مقادیری قابل انتقال هستند

به‌عنوان Prop برای یک Client Component، فقط انواع زیر مجازند:

  • مقادیر اولیه: رشته، عدد، bigint، Boolean، undefined، null و Symbolهایی که با Symbol.for در رجیستری سراسری ثبت شده‌اند.
  • مجموعه‌ها (Iterable): Array، Map، Set، TypedArray و ArrayBuffer، به شرط اینکه محتوایشان هم قابل انتقال باشد.
  • Date.
  • Objectهای ساده: آبجکتی که با نحو { ... } ساخته شده و ویژگی‌هایش قابل انتقال باشند.
  • JSX: یعنی المان‌های Server Component یا Client Component، که الگوی children درس قبل بر همین پایه کار می‌کند.
  • Promise: که در Client Component با use (درس ۲۳-۸) خوانده می‌شود.
  • Server Functionها: توابعی که با "use server" علامت‌گذاری شده‌اند.

چه چیزهایی قابل انتقال نیستند

  • توابع معمولی: تابعی که در Server Component تعریف شده و Server Function نیست.
  • نمونه‌های کلاس: هر آبجکتی که از یک کلاس (غیر از انواع داخلی مثل Date و Map) ساخته شده باشد، یا آبجکتی با Prototype برابر null.
  • Symbolهای ثبت‌نشده در رجیستری سراسری.

مثال رایج: پاس دادن Event Handler

jsx
// Page.js (Server Component)
import Card from "./Card"; // Client Component

export default function Page() {
  function handleClick() {
    console.log("کلیک شد");
  }

  // نادرست: تابع معمولی از مرز عبور نمی‌کند
  return <Card onClick={handleClick} />;
}

این کد خطا می‌دهد، چون تابع handleClick یک تابع معمولی جاوااسکریپتی است که روی سرور تعریف شده و راهی برای فرستادنش به مرورگر وجود ندارد. دو راه درست:

  • نوشتن Handler داخل Client Component: اگر کار فقط تعاملی و محلی است (مثل باز و بسته کردن یک منو)، همان تابع را داخل Card بنویسید.
  • استفاده از Server Function: اگر باید یک عملیات روی سرور اجرا شود (مثل ذخیره در پایگاه داده)، تابع را با "use server" علامت‌گذاری کنید. در این حالت، به‌جای خود تابع، یک ارجاع به آن منتقل می‌شود و Client Component هنگام صدا زدن، یک درخواست به سرور می‌فرستد.

چیزی که Prop می‌شود، برای کاربر قابل مشاهده است

این یک نکته امنیتی مهم است. هر چیزی که به یک Client Component داده می‌شود، در مرورگر کاربر هم دیده می‌شود و می‌تواند در پاسخ شبکه بازرسی شود. بنابراین، مثلاً یک ردیف کامل از جدول کاربران را (که ممکن است شامل هش رمز یا اطلاعات داخلی باشد) به‌عنوان Prop ندهید. فقط فیلدهایی را بدهید که Client Component واقعاً لازم دارد:

jsx
const user = await db.users.find(id);

// نادرست: کل ردیف پایگاه داده
<Profile user={user} />

// درست: فقط فیلدهای موردنیاز
<Profile name={user.name} avatar={user.avatar} />

حجم داده هم اهمیت دارد

Propهای بزرگ، حجم داده‌ای که روی شبکه فرستاده می‌شود را زیاد می‌کنند، و این دقیقاً چیزی است که Server Components قرار بود به کاهش آن کمک کنند. قاعده خوب: اگر فقط بخش کوچکی از یک داده بزرگ نیاز است، همان بخش را روی سرور جدا کنید و به Client Component بدهید، نه کل داده را.

Promise به‌عنوان Prop

یک الگوی مهم این است که Server Component یک Promise را شروع کند و بدون await آن را به Client Component بدهد. Client Component همان Promise را با use می‌خواند و تا حل شدن آن، نزدیک‌ترین Suspense (فصل بیست‌ویکم) نمایش داده می‌شود. این الگو پایه چیزی است که در درس ۲۴-۷ درباره Streaming می‌بینیم.

چرا این درس مثال قابل اجرا ندارد

Serialization بین Server و Client فقط در یک پروژه با Framework یا Bundler پشتیبان Server Components اتفاق می‌افتد، که در ویرایشگر درون‌صفحه‌ای نیست. کدهای این درس را در یک پروژه واقعی اجرا کنید تا پیام‌های خطای واقعی را ببینید.

جمع‌بندی

Propهایی که از Server Component به Client Component می‌روند باید قابل Serialization باشند: مقادیر اولیه، Array و Map و Set، Date، Objectهای ساده، JSX، Promise و Server Function. توابع معمولی و نمونه‌های کلاس مجاز نیستند. هر چیزی که به Client Component می‌دهید در مرورگر قابل مشاهده است، پس فقط فیلدهای لازم را انتقال دهید. در درس بعدی، به تعامل‌پذیری در سمت Client می‌پردازیم.