ترکیب کامپوننت‌ها: ساختن رابط کاربری از قطعات کوچک‌تر

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

ترکیب یعنی چه

ترکیب یعنی ساختن یک کامپوننت بزرگ‌تر با قرار دادن چند کامپوننت کوچک‌تر در کنار یا داخل یکدیگر. این دقیقاً همان کاری است که در مثال‌های قبلی هم انجام داده‌ایم؛ مثلاً وقتی ProductCard را داخل ProductList استفاده کردیم.

function ProductPage() {
  return (
    <div>
      <SearchBar />
      <ProductList />
      <Pagination />
    </div>
  );
}

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

چرا React به‌جای وراثت از ترکیب استفاده می‌کند

در بسیاری از زبان‌های شیءگرا، برای اشتراک‌گذاری رفتار بین کلاس‌ها از وراثت (Inheritance) استفاده می‌شود؛ یعنی یک کلاس، ویژگی‌ها و رفتار کلاس دیگری را به ارث می‌برد. React برای اشتراک‌گذاری قابلیت بین کامپوننت‌ها، به‌جای وراثت، بر پایه ترکیب طراحی شده است.

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

ترکیب از طریق children

یکی از قدرتمندترین ابزارهای ترکیب، همان children است که در فصل قبل دیدیم. با استفاده از children، یک کامپوننت می‌تواند «قالب» را تعیین کند، بدون این‌که بداند دقیقاً چه محتوایی داخل آن قرار می‌گیرد:

function Layout({ children }) {
  return (
    <div className="layout">
      <Header />
      <main>{children}</main>
      <Footer />
    </div>
  );
}

حالا می‌توان Layout را با محتوای متفاوت، در صفحات مختلف استفاده کرد:

function HomePage() {
  return (
    <Layout>
      <h2>خوش آمدید</h2>
    </Layout>
  );
}

function AboutPage() {
  return (
    <Layout>
      <h2>درباره ما</h2>
    </Layout>
  );
}

در این مثال، منطق مشترک (هدر و فوتر) فقط یک‌بار در Layout نوشته شده و هر صفحه فقط محتوای اختصاصی خودش را مشخص می‌کند.

ترکیب از طریق Props که خودشان JSX هستند

گاهی یک کامپوننت به بیش از یک ناحیه محتوا نیاز دارد، نه فقط یک children. در این حالت می‌توان از Propهایی استفاده کرد که مقدارشان خودش یک قطعه JSX است:

function SplitPanel({ left, right }) {
  return (
    <div className="split">
      <div className="left">{left}</div>
      <div className="right">{right}</div>
    </div>
  );
}

function App() {
  return (
    <SplitPanel
      left={<p>محتوای سمت چپ</p>}
      right={<p>محتوای سمت راست</p>}
    />
  );
}

این الگو زمانی مفید است که یک کامپوننت باید چند ناحیه مجزا از محتوای دلخواه را در جاهای مشخصی از ساختار خودش قرار دهد.

مثال قابل اجرا

ترکیب کامپوننت‌ها با children
برای دیدن پیش‌نمایش کامپوننت، روی Run بزنید.

جمع‌بندی

ترکیب کامپوننت‌ها یعنی ساختن رابط کاربری‌های بزرگ‌تر از کنار هم قرار دادن کامپوننت‌های کوچک‌تر، معمولاً از طریق nesting مستقیم یا استفاده از children و Propهای JSX. React به‌جای وراثت، بر این الگو تکیه دارد چون انعطاف بیشتری در تعیین رابطه بین کامپوننت‌ها فراهم می‌کند. در درس بعدی به مسئولیت‌های کامپوننت و طراحی مناسب Props می‌پردازیم.