React یک کتابخانه محبوب جاوااسکریپت برای ساخت رابط‌های کاربری است و ابزارهای مختلفی را برای مدیریت state و جریان داده در یک برنامه در اختیار توسعه‌دهندگان قرار می‌دهد. یکی از این ابزار، هوک useContext است که به توسعه‌دهندگان این امکان را می‌دهد تا به راحتی داده‌ها را در چندین کامپوننت بدون نیاز به prop drilling به اشتراک بگذارند.

در این مقاله قصد داریم هوک useContext را با جزئیات بررسی کنیم و نحوه استفاده موثر از آن در برنامه‌های React را یاد بگیریم.

منظور از Context در React چیست؟

مفهوم context در React به مکانیزمی اشاره دارد که اجازه می‌دهد داده‌ها در چندین کامپوننت به اشتراک گذاشته شوند، بدون اینکه صریحاً از طریق props ارسال شوند. context روشی را برای انتقال داده‌ها از طریق درخت کامپوننت بدون نیاز به ارسال props به صورت دستی در هر سطح فراهم می‌کند. استفاده از context به ویژه زمانی مفید است که با داده‌های سراسری یا داده‌هایی که باید توسط بسیاری از کامپوننت‌های برنامه قابل دسترسی باشند، سروکار داریم.

چرا استفاده از هوک useContext مهم است؟

به اشتراک گذاری داده‌ها در میان کامپوننت‌ها

یکی از مزایای اصلی استفاده از context، امکان به اشتراک گذاری داده‌ها در چندین کامپوننت است. این امر به ویژه هنگام برخورد با داده‌هایی که نیاز به دسترسی به چندین کامپوننت در سطوح مختلف درخت کامپوننت دارند، بسیار مفید می‌باشد.

اجتناب از Prop Drilling

Prop drilling فرآیند انتقال داده‌ها از یک کامپوننت به کامپوننت دیگر از طریق props است. هنگامی که داده‌ها باید از چندین سطح از کامپوننت‌های مختلف منتقل شوند، این موضوع می‌تواند کمی پیچیده بوده و مدیریت آن دشوار باشد. context با ارائه روشی متمرکز برای به اشتراک گذاری داده‌ها، نیاز به Prop drilling را از بین می‌برد.

روش ساخت یک Context در React

برای ساخت یک context در React از متد React.createContext استفاده می‌کنیم. این متد یک آبجکت context را return می‌کند که می‌تواند برای تهیه و مصرف مقادیر در درخت کامپوننت استفاده شود.

استفاده از هوک useContext

دسترسی به مقادیر Context

هوک useContext این امکان را به ما می‌دهد تا به مقدار یک context در یک کامپوننت فانکشنال دسترسی پیدا کنیم. با ارسال آبجکت context به هوک useContext، می‌توانیم مقدار فعلی context را بازیابی کنیم.

به‌روزرسانی مقادیر Context

برای به‌روزرسانی مقدار یک context، از context provider استفاده می‌کنیم. provider به ما اجازه می‌دهد تا مقداری را تعریف کنیم که باید در دسترس همه کامپوننت‌هایی باشد که از context استفاده می‌کنند.

بررسی مثال عملیTheme Switcher

ساخت Context تم

در این بخش قصد داریم تا مفهوم context را در یک مثال عملی با عنوان theme switcher بیشتر بررسی کنیم. ابتدا یک آبجکت context برای ذخیره تم فعلی ایجاد می‌کنیم.

import React, { useContext, useState } from 'react';

// Define the shape of the context data using a TypeScript interface
interface ThemeContextData {
  theme: string;
  toggleTheme: () => void;
}

// Create the context with an initial value and the TypeScript interface
const ThemeContext = React.createContext<ThemeContextData>({
  theme: 'light',
  toggleTheme: () => {},
});

افزودن Theme Provider

سپس یک کامپوننت ThemeProvider اضافه می‌کنیم که مقدار context را به کامپوننت‌های child ارائه می‌دهد.

export interface IThemeProviderProps {
  children: React.ReactNode;
}

// Create a ThemeProvider component to provide the context value to child components
export const ThemeProvider: React.FC<IThemeProviderProps> = ({ children } ) => {
  const [theme, setTheme] = useState("light");

  // Function to toggle the theme between light and dark
  const toggleTheme = () => {
    setTheme((prevTheme) => (prevTheme === "light" ? "dark" : "light"));
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }} >
      <div className = {theme == 'light' ? 'lightTheme': 'darkTheme'}>
        {children}
      </div>
    </ThemeContext.Provider>
    
  );
};

پیاده‌سازی کامپوننت child مربوط به Theme Switcher

در مرحله بعد، ما یک کامپوننت تغییر تم ایجاد می‌کنیم. این کامپوننت به کاربران اجازه می‌دهد تا با فشار دادن یک دکمه، بین تم‌های light و dark جابجا شوند. این کامپوننت از ThemeContext به کمک هوک useContext بهره‌مند می‌شود.

// Create a ThemeSwitcher component that consumes the context value
export const ThemeSwitcher: React.FC = () => {
  // Use the useContext hook to access the context value
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <div >
      <p className='themeTitleText'>Current theme: {theme}</p>
      <button onClick={toggleTheme} className={theme + 'ButtonTheme'} >Toggle Theme</button>
    </div>
  );
};

استفاده از ThemeProvider

در همه برنامه‌های React، یک کامپوننت فانکشنال به نام App داریم که به عنوان کامپوننت اصلی برنامه React عمل می‌کند. این کامپوننت App مسئول رندر کردن کامپوننت ThemeSwitcher است که یک کامپوننت child می‌باشد و به کاربران این امکان را می‌دهد تا بین تم‌های مختلف (مانند تم‌های light و dark) جابجا شوند.

کامپوننت ThemeProvider یک context provider است که برای در دسترس قرار دادن داده‌ها و عملکردهای مرتبط با تم برای همه کامپوننت‌های child خود استفاده می‌شود. این کامپوننت با استفاده از React context API ایجاد شده است و مسئول مدیریت state تم و ارائه تابعی برای تغییر آن می‌باشد.

کامپوننت ThemeSwitcher در داخل کامپوننت ThemeProvider قرار گرفته است، به این معنی که کامپوننت ThemeSwitcher، و هر کامپوننت child دیگر در ThemeProvider، به context تم دسترسی خواهد داشت. این دسترسی با استفاده از هوک useContext به دست می‌آید.

در کد زیر مشاهده می‌کنیم که چگونه کامپوننت child، یعنی ThemeSwitcher را داخل ThemeProvider قرار می‌دهیم. تمام این اتفاقات در کامپوننت App.tsx برنامه ما انجام می‌شود.

// Use the ThemeProvider to wrap the ThemeSwitcher component
const App: React.FC = () => {
  return (
      <div className='ThemeSwitcherContainer'>
        <ThemeProvider>        
          <ThemeSwitcher />        
        </ThemeProvider>
      </div>
  );
};

در ادامه کد CSS هم داریم که برای واضح‌تر کردن جابجایی بین تم‌ها کاربر دارد. این CSS شامل مجموعه‌ای از استایل‌ها برای container اصلی برنامه و همچنین تم light و dark برای هر کامپوننت دکمه است:

.lightTheme {
    background-color: #fff;
    color: #000;
    height: 350px;
    margin-left: 50px;
    margin-right: 50px;
    display: flex;
    flex-direction: row;
    justify-content: center;
}

.darkTheme {
    background-color: #000;
    color: #fff;
    height: 350px;
    margin-left: 50px;
    margin-right: 50px;
    display: flex;
    flex-direction: row;
    justify-content: center;
    justify-content: center;
}

.darkButtonTheme {
    background-color: #000;
    color: #fff;
    border-radius: 5px;
    border: 1px solid #fff;  
    display: flex;
    flex-direction: row;
    justify-content: center;
   
}

.darkButtonTheme:hover {
    background-color: #ddd; /* Change the background color on hover */
    color: #000;
    cursor: pointer; /* Change the cursor to a pointer on hover */
}


.lightButtonTheme {
    background-color: #fff;
    color: #000;
    border-radius: 5px;
    border: 1px solid #000;
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-content: center;
}

.lightButtonTheme:hover {
    background-color: #ddd; /* Change the background color on hover */
    cursor: pointer; /* Change the cursor to a pointer on hover */
}

.themeTitleText {
    text-align: center;
    margin-top: 50px;
    margin-bottom: 50px;
    font-size: 60px;
}

مقایسه useContext و Redux

useContext و Redux هر دو ابزارهای محبوبی برای مدیریت state در برنامه‌های React هستند، اما اهداف و موارد استفاده متفاوتی دارند. در این بخش قصد داریم تا تفاوت‌های بین useContext و Redux و همچنین بهترین زمان استفاده از هر کدام را بررسی نماییم.

useContext

useContext یک هوک React است که راهی برای به اشتراک گذاشتن داده‌ها (context) در چندین کامپوننت بدون ارسال مستقیم طریق props را فراهم می‌کند. این هوک بخشی از React Context API می‌باشد که در کتابخانه React تعبیه شده است.

Redux

Redux یک کتابخانه مدیریت state مستقل است که می‌تواند با هر برنامه جاوااسکریپتی از جمله React استفاده شود. این کتابخانه یک store متمرکز برای مدیریت state سراسری فراهم می‌کند و از یک جریان داده یک‌طرفه دقیق پیروی می‌نماید.

چه زمانی باید از کدام روش استفاده کنیم؟

به طور خلاصه، در حالی که هوک useContext یک راه‌حل ساده و راحت برای به اشتراک‌گذاری state در یک برنامه React ارائه می‌کند، اما Redux راه‌حل قوی‌تر و مقیاس‌پذیرتری را برای مدیریت state پیچیده سراسری ارائه می‌دهد. در نهایت، انتخاب بین هوک useContext و کتابخانه Redux به نیازهای خاص و مقیاس برنامه‌ای که داریم بستگی دارد.

موارد استفاده رایج برای هوک useContext

احراز هویت کاربر

می‌توانیم از context برای مدیریت state احراز هویت کاربر و ارائه داده‌های خاص کاربر به کامپوننت‌هایی که به آن نیاز دارند، استفاده کنیم.

محلی‌سازی زبان

استفاده از context برای پیاده‌سازی محلی‌سازی زبان با ارائه رشته‌های ترجمه‌شده به کامپوننت‌ها بر اساس زبانی که کاربر ترجیح داده است، بسیار مفید می‌باشد.

سفارشی‌سازی تم

می‌توانیم از context برای مدیریت و اعمال تم‌های سفارشی بر روی یک برنامه استفاده کنیم و به کاربران این امکان را فراهم کنیم که ظاهر برنامه را شخصی‌سازی کنند.

محدودیت‌های هوک useContext

یکی از محدودیت‌های هوک useContext این است که بهینه‌سازی عملکرد داخلی ندارد. وقتی مقدار ارائه‌شده توسط یک context provider تغییر می‌کند، همه کامپوننت‌هایی که از context استفاده می‌کنند، بدون توجه به اینکه تغییر مربوط به آن‌ها می‌باشد یا خیر، مجددا رندر می‌شوند. این موضوع می‌تواند منجر به ایجاد رندرهای غیرضروری شود و به ویژه در برنامه‌های بزرگ با به‌روزرسانی‌های مکرر state، تأثیر منفی بر روی عملکرد بگذارد. توسعه‌دهندگان برای جلوگیری از ایجاد رندرهای غیرضروری در کامپوننت‌های child، باید بهینه‌سازی‌های عملکردی خود را پیاده‌سازی کنند.

جمع‌بندی

هوک useContext در React روشی مناسب برای به اشتراک گذاشتن داده‌ها در بین کامپوننت‌ها بدون prop drilling فراهم می‌کند. توسعه‌دهندگان با درک چگونگی ایجاد و استفاده از context می‌توانند برنامه‌های مقیاس‌پذیر با قابلیت نگه‌داری بالا بسازند.