Component Tree: ساختار درختی که React با آن کار می‌کند

در دو درس قبل دیدیم React چگونه خروجی یک کامپوننت را محاسبه (Render) و سپس در DOM اعمال (Commit) می‌کند. اما یک کامپوننت به‌تنهایی وجود ندارد؛ همیشه بخشی از یک ساختار بزرگ‌تر است. در این درس به Component Tree می‌پردازیم: ساختاری که React برای سازمان‌دهی و ردیابی همه کامپوننت‌های یک برنامه استفاده می‌کند.

از JSX تودرتو تا یک درخت

در فصل اول دیدیم وقتی چند کامپوننت داخل یکدیگر استفاده می‌شوند، یک ساختار درختی شکل می‌گیرد. حالا که با Render و Commit آشنا شده‌ایم، می‌توانیم این درخت را دقیق‌تر ببینیم:

function App() {
  return (
    <Layout>
      <Header />
      <Sidebar>
        <UserBadge />
      </Sidebar>
    </Layout>
  );
}
App
 └─ Layout
     ├─ Header
     └─ Sidebar
         └─ UserBadge

هر گره این درخت، یک نمونه (Instance) از یک کامپوننت است، نه فقط تعریف تابع آن. یعنی اگر UserBadge در دو جای مختلف درخت استفاده شود، هرکدام گره جداگانه‌ای هستند، با State و موقعیت کاملاً مستقل از هم.

هر گره، State و هویت خودش را دارد

React برای هر گره این درخت، اطلاعاتی نگه می‌دارد: State فعلی آن، Refهای متصل به آن، و موقعیت دقیقش در درخت. همین موقعیت در درخت است که در فصل دوازدهم، هنگام بررسی حفظ و بازنشانی State، نقش اصلی را بازی می‌کند؛ یعنی اینکه React یک کامپوننت را «همان کامپوننت قبلی» بداند یا «یک نمونه کاملاً جدید»، به موقعیت آن در همین درخت وابسته است.

رندر یک گره، یعنی رندر زیردرخت آن

وقتی گفته می‌شود یک کامپوننت Render می‌شود، معمولاً به این معناست که تمام زیردرخت زیر آن هم، به‌طور پیش‌فرض، دوباره Render می‌شود. اگر App دوباره رندر شود، React بدنه Layout، Header، Sidebar و UserBadge را هم دوباره اجرا می‌کند، مگر اینکه بهینه‌سازی خاصی (مانند آنچه در فصل سیزدهم با React.memo می‌بینیم) این رفتار پیش‌فرض را تغییر داده باشد.

درخت React، همان درخت DOM نیست

یک نکته مهم این است که Component Tree با DOM Tree یکی نیست. یک کامپوننت می‌تواند هیچ خروجی DOM مستقیمی نداشته باشد (مثلاً وقتی فقط children خود را برمی‌گرداند یا از Fragment استفاده می‌کند)، اما همچنان یک گره مستقل در Component Tree محسوب می‌شود. React این دو ساختار را جداگانه نگه می‌دارد و از روی Component Tree، DOM Tree نهایی را می‌سازد.

چرا این مدل ذهنی مفید است

فکر کردن به یک برنامه React به‌شکل یک درخت، به فهم چند موضوع کمک می‌کند: چرا تغییر State در یک گره بالا، معمولاً زیردرخت زیرش را هم رندر می‌کند؛ چرا موقعیت یک کامپوننت در این درخت (نه فقط نام تابعش) هویت آن را مشخص می‌کند؛ و چرا Context، که در فصل هشتم دیدیم، دقیقاً بر اساس همین ساختار درختی، مقدار را از یک گره به تمام زیرگره‌هایش می‌رساند.

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

در این مثال، دو نمونه از یک کامپوننت در دو شاخه مختلف درخت رندر شده‌اند. با کلیک روی هرکدام، فقط همان نمونه تغییر می‌کند و نمونه دیگر دست‌نخورده می‌ماند.

دو نمونه مستقل از یک کامپوننت در درخت
برای دیدن پیش‌نمایش کامپوننت، روی Run بزنید.

جمع‌بندی

Component Tree ساختاری است که React برای نگهداری State، Ref و موقعیت هر نمونه از هر کامپوننت استفاده می‌کند؛ این درخت با DOM Tree یکی نیست، هرچند از روی آن ساخته می‌شود. رندر یک گره، به‌طور پیش‌فرض یعنی رندر کل زیردرخت آن. در درس بعدی به Reconciliation می‌پردازیم: فرایندی که React با آن مشخص می‌کند بین دو نسخه از این درخت، دقیقاً چه چیزی تغییر کرده است.