Provider و useContext: تعیین و مصرف مقدار Context

در درس قبلی با createContext یک Context ساختیم، اما گفتیم که این کار به‌تنهایی هیچ مقداری را در اختیار کسی قرار نمی‌دهد. در این درس، دو قطعه باقی‌مانده پازل را می‌بینیم: Provider که مقدار Context را برای یک بخش از درخت کامپوننت‌ها تعیین می‌کند، و useContext که آن مقدار را در هر کامپوننت مصرف‌کننده می‌خواند.

Provider: تعیین مقدار برای یک زیردرخت

هر Context، همراه با خودش یک کامپوننت به نام Provider دارد. هر کامپوننتی که داخل این Provider قرار گیرد (در هر عمقی)، به مقداری که از طریق ویژگی value تعیین شده، دسترسی خواهد داشت:

const UserContext = createContext(null);

function App() {
  const user = { name: "سارا" };

  return (
    <UserContext.Provider value={user}>
      <Page />
    </UserContext.Provider>
  );
}

در این کد، user برای هر کامپوننتی که در هر عمقی داخل Page رندر می‌شود، از طریق Context در دسترس است؛ بدون این‌که Page یا هیچ‌کدام از فرزندان میانی، لازم باشد آن را از طریق Props منتقل کنند.

useContext: خواندن مقدار در کامپوننت مصرف‌کننده

در هر کامپوننتی که به این مقدار نیاز داریم، از useContext استفاده می‌کنیم:

function UserBadge() {
  const user = useContext(UserContext);
  return <p>{user.name}</p>;
}

نکته مهم این است که UserBadge اصلاً نیازی ندارد بداند این مقدار از کجا آمده یا چند لایه بالاتر تعیین شده است؛ فقط کافی است داخل یک UserContext.Provider رندر شده باشد.

ترکیب کامل: از Provider تا مصرف‌کننده

const UserContext = createContext(null);

function App() {
  const user = { name: "سارا" };
  return (
    <UserContext.Provider value={user}>
      <Page />
    </UserContext.Provider>
  );
}

function Page() {
  return <Sidebar />;
}

function Sidebar() {
  return <UserBadge />;
}

function UserBadge() {
  const user = useContext(UserContext);
  return <p>{user.name}</p>;
}

با مقایسه این کد با مثال درس قبل، می‌بینیم که دیگر نیازی نیست Page و Sidebar اصلاً درباره user چیزی بدانند یا آن را به‌عنوان Prop دریافت کنند.

چه اتفاقی می‌افتد اگر Provider وجود نداشته باشد

اگر یک کامپوننت از useContext استفاده کند، اما هیچ Providerی در سطوح بالاتر آن وجود نداشته باشد، مقدار بازگردانده‌شده، همان مقدار پیش‌فرضی خواهد بود که در createContext مشخص کرده‌ایم. به همین دلیل، انتخاب یک مقدار پیش‌فرض معنادار (یا null به‌عنوان نشانه‌ای از «هنوز تعیین نشده»)، در createContext اهمیت دارد.

می‌توان چند Context مختلف را با هم ترکیب کرد

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

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

Provider و useContext در عمل
برای دیدن پیش‌نمایش کامپوننت، روی Run بزنید.

جمع‌بندی

یک Context با دو بخش کامل می‌شود: Provider که مقدار را برای یک زیردرخت مشخص تعیین می‌کند، و useContext که آن مقدار را در هر کامپوننت مصرف‌کننده، در هر عمقی، می‌خواند. کامپوننت‌های میانی هیچ نیازی به آگاهی یا انتقال دستی این مقدار ندارند. در درس بعدی به این می‌پردازیم که تغییر مقدار Context، چه اثری روی رندر مجدد کامپوننت‌های مصرف‌کننده دارد.