State Colocation: State باید کجا زندگی کند؟

تا اینجا یاد گرفتیم چه زمانی باید State تعریف کنیم و چه زمانی نباید. اما یک سؤال دیگر هم مطرح است: وقتی به یک State واقعی نیاز داریم، آن را در کدام کامپوننت تعریف کنیم؟ پاسخ درست به این سؤال، موضوع اصلی این درس است: State Colocation.

یک اشتباه رایج: بالا بردن بیش‌ازحد State

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

function App() {
  const [searchText, setSearchText] = useState("");

  return (
    <div>
      <Header />
      <SearchBox value={searchText} onChange={setSearchText} />
      <Footer />
    </div>
  );
}

مشکل این است که searchText فقط توسط SearchBox استفاده می‌شود، اما چون در App تعریف شده، هر بار که کاربر تایپ می‌کند، کل App و در نتیجه تمام فرزندانش (از جمله Header و Footer که هیچ ربطی به جست‌وجو ندارند) دوباره رندر می‌شوند.

راه‌حل: نگه‌داشتن State نزدیک به جایی که استفاده می‌شود

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

function SearchBox() {
  const [searchText, setSearchText] = useState("");

  return (
    <input
      value={searchText}
      onChange={(e) => setSearchText(e.target.value)}
    />
  );
}

function App() {
  return (
    <div>
      <Header />
      <SearchBox />
      <Footer />
    </div>
  );
}

حالا با هر تغییر در searchText، فقط SearchBox دوباره رندر می‌شود، نه کل درخت زیر App.

رابطه Colocation با عملکرد برنامه

چون در React، تغییر State معمولاً باعث رندر مجدد همان کامپوننت و تمام فرزندانش می‌شود (جزئیات دقیق این موضوع را در فصل مربوط به مدل رندرینگ بررسی می‌کنیم)، نگه‌داشتن State در سطحی بالاتر از نیاز واقعی، می‌تواند باعث رندرهای غیرضروری در بخش‌هایی از برنامه شود که هیچ ارتباطی با آن تغییر ندارند. Colocation با محدود کردن دامنه تأثیر یک تغییر State، از این رندرهای اضافی جلوگیری می‌کند.

Colocation در تضاد با Lifting State نیست

ممکن است این درس با چیزی که پیش‌تر درباره Lifting State Up شنیده باشید (موضوع درس بعدی) متناقض به نظر برسد. اما این دو مفهوم مکمل هم هستند، نه در تضاد: Lifting State Up زمانی لازم است که چند کامپوننت واقعاً به یک State مشترک نیاز دارند. Colocation می‌گوید اگر فقط یک کامپوننت به آن نیاز دارد، آن را بالاتر از حد لازم نبرید. قاعده کلی این است: State را تا جایی بالا ببرید که همه مصرف‌کنندگان واقعی آن به آن دسترسی داشته باشند؛ نه بیشتر.

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

نگه‌داشتن State نزدیک به محل استفاده
برای دیدن پیش‌نمایش کامپوننت، روی Run بزنید.

جمع‌بندی

State Colocation یعنی نگه‌داشتن State در پایین‌ترین سطح ممکن از درخت کامپوننت‌ها که همه مصرف‌کنندگان واقعی آن همچنان به آن دسترسی دارند. این کار از رندرهای غیرضروری در بخش‌هایی از برنامه که به آن State نیازی ندارند جلوگیری می‌کند. در درس بعدی به مفهوم مکمل آن، یعنی Lifting State Up، می‌پردازیم.