هویت کامپوننت و Position در Tree

در فصل قبل با Reconciliation و این اصل آشنا شدیم که React هویت یک گره را از روی نوع و موقعیتش در درخت تشخیص می‌دهد. در این فصل، این ایده را عمیق‌تر دنبال می‌کنیم و می‌بینیم دقیقاً چگونه State یک کامپوننت، بر اساس همین هویت، حفظ یا بازنشانی می‌شود.

یادآوری کوتاه: هویت از کجا می‌آید

همان‌طور که در فصل یازدهم دیدیم، React برای هر کامپوننت رندرشده، به دو چیز نگاه می‌کند: نوع آن (کدام تابع کامپوننت) و موقعیت آن در ساختار JSX والدش. اگر هر دو این‌ها بین دو رندر یکسان بمانند، React نتیجه می‌گیرد این همان نمونه قبلی است و State آن را حفظ می‌کند.

«همان جای کد» با «همان جای درخت» یکی نیست

نکته‌ای که گاهی اشتباه فهمیده می‌شود این است که «Position» به معنای خط کد نیست، بلکه به معنای جایگاه در ساختار درختی نهایی JSX است. دو مثال زیر را مقایسه کنید:

// حالت اول: یک شرط، دو موقعیت متفاوت
function Page({ isAdmin }) {
  return (
    <div>
      {isAdmin ? <AdminPanel /> : <UserPanel />}
    </div>
  );
}

در این کد، فقط یک div وجود دارد، اما داخل آن دو نوع کامپوننت متفاوت (AdminPanel یا UserPanel) به‌جای هم قرار می‌گیرند. از دید React، این دو، عناصر متفاوتی در همان موقعیت هستند؛ با تغییر isAdmin، نوع عوض می‌شود و طبق قاعده Reconciliation، نمونه قدیمی حذف و نمونه جدید از صفر ساخته می‌شود.

یک مثال ظریف‌تر: همان کامپوننت در دو شاخه مختلف JSX

function Page({ isAdmin }) {
  if (isAdmin) {
    return (
      <div>
        <Counter />
        <AdminTools />
      </div>
    );
  }

  return (
    <div>
      <Counter />
    </div>
  );
}

در این کد، در هر دو شاخه if، یک Counter در همان موقعیت اول div قرار دارد. با اینکه کد در دو جای متفاوت نوشته شده، از دید درخت نهایی JSX، هر دو Counter در «فرزند اول همان div» هستند؛ پس React معمولاً همان نمونه را حفظ می‌کند و با تغییر isAdmin، State این Counter از بین نمی‌رود.

وقتی ساختار اطراف عوض می‌شود، هویت هم می‌تواند عوض شود

اگر ساختار والد به‌قدری تغییر کند که موقعیت یک کامپوننت در درخت واقعاً فرق کند (نه فقط شرط کد)، React دیگر آن را همان نمونه قبلی نمی‌داند. برای مثال، اگر یک Wrapper جدید فقط در یکی از دو حالت دور Counter اضافه شود، موقعیت واقعی آن در درخت تغییر کرده و State از بین می‌رود؛ حتی با اینکه هر دو Counter در نگاه اول «همان کامپوننت» به نظر می‌رسند.

چرا این جزئیات اهمیت دارد

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

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

در این مثال، Counter در هر دو حالت isAdmin، در همان موقعیت اول والد قرار دارد. مقدار شمارنده را افزایش دهید و سپس نقش را تغییر دهید تا ببینید State حفظ می‌شود.

حفظ State یک کامپوننت در دو شاخه شرطی هم‌موقعیت
برای دیدن پیش‌نمایش کامپوننت، روی Run بزنید.

جمع‌بندی

هویت یک کامپوننت از ترکیب نوع آن و موقعیتش در درخت نهایی JSX تعیین می‌شود، نه از اینکه در کدام خط یا شاخه از کد جاوااسکریپت نوشته شده است. همان کامپوننت می‌تواند در دو شاخه مختلف کد، ولی در یک موقعیت یکسان از درخت باشد و State خود را حفظ کند. در درس بعدی، مستقیم‌تر به مفهوم Keyها در ارتباط با همین هویت می‌پردازیم.