useContext: ماهیت، هوک دسترسی به کنتکست و پروایدرها در React

نویسنده: IT Sectr منتشر شده: 2026-07-04 زمان مطالعه: 9 دقیقه

useContext — یک هوک React است که به کامپوننت‌های تابعی دسترسی مستقیم به داده‌های کنتکستی که از طریق createContext ایجاد شده است فراهم می‌کند. کنتکست در React مشکل props drilling را حل می‌کند — انتقال props از طریق چندین کامپوننت واسطه که خود از این داده‌ها استفاده نمی‌کنند. به پیرو از React Documentation (2025)، useContext شیئ کنتکست را می‌پذیرد و مقدار جاری تعیین‌شده توسط نزدیک‌ترین Provider در بالای درخت کامپوننت‌ها را برمی‌گرداند. هنگامی که مقدار در Provider تغییر می‌کند، تمامی کامپوننت‌هایی که از useContext استفاده می‌کنند به‌طور خودکار مجدداً رندر می‌شوند.

نکات کلیدی

  • useContext — هوکی برای خواندن مقدار از کنتکست React بدون props drilling.
  • createContext — شیئ کنتکست را با مقدار پیش‌فرض و کامپوننت Provider ایجاد می‌کند.
  • Provider — کامپوننتی که مقدار کنتکست را به تمامی عناصر فرزند منتقل می‌کند.
  • رندر مجدد — تغییر مقدار Provider باعث رندر مجدد تمامی مصرف‌کنندگان کنتکست می‌شود.
  • عبور از کامپوننت‌های واسطه — useContext امکان انتقال داده‌ها را از طریق چندین سطح تودرو فراهم می‌کند.

useContext در React چیست

useContext — هوکی است که در React 16.8 همراه با سایر هوک‌ها اضافه شد و به شما امکان خواندن مقدار از کنتکست React را می‌دهد. کنتکست یک مکانیسم داخلی در React است که برای انتقال داده‌ها از طریق درخت کامپوننت‌ها بدون نیاز به انتقال دستی props در هر سطح طراحی شده است. useContext جایگزین کامپوننت Consumer از Context API قدیمی شده و کد را موجزتر و خواناتر می‌کند.

سناریوهای تیپیک استفاده از کنتکست شامل موضوع‌های ظاهری (روشن/تیره)، محلی‌سازی و ترجمه (i18n)، احراز هویت کاربر، تنظیمات برنامه و سایر داده‌های سراسری که در سطوح مختلف تودرو به کامپوننت‌های متعدد نیاز دارند. تیم React توصیه می‌کند از کنتکست برای داده‌هایی استفاده کنید که برای زیردرخت کامپوننت‌ها سراسری هستند، اما نه برای کل برنامه.

به پیرو از React Team — Context documentation (2025)، استفاده نادرست از کنتکست یکی از علل اصلی مشکلات کارایی در برنامه‌های React است. هر تغییر مقدار در Provider باعث رندر مجدد تمامی مصرف‌کنندگان می‌شود، بدون توجه به اینکه کدام بخش از داده‌ها تغییر کرده است. بهینه‌سازی از طریق useMemo و تقسیم کنتکست‌ها این مشکل را حل می‌کند.

jsx
import { createContext, useContext } from 'react';

// ایجاد کنتکست با مقدار پیش‌فرض
const ThemeContext = createContext('روشن');

function ThemedButton() {
    const theme = useContext(ThemeContext);
    return <button className={`btn-${theme}`}>Click</button>;
}

کنتکست در React چگونه کار می‌کند

مکانیسم کنتکست در React از طریق الگوی Provider-Consumer پیاده‌سازی شده است. createContext شیئی را با دو واجد بازمی‌گرداند: Provider — کامپوننتی که مقدار را منتقل می‌کند، و خود شیئ کنتکست که در useContext استفاده می‌شود. Provider در درخت کامپوننت‌ها نصب می‌شود و مقدار را به تمامی عناصر فرزند بدون توجه به عمق تودرو منتقل می‌کند.

وقتی React با فراخوانی useContext مواجه می‌شود، به بالای درخت fiber حرکت می‌کند تا نزدیک‌ترین Provider را برای آن کنتکست پیدا کند. اگر Provider پیدا شود، مقدار آن برگشته می‌شود. اگر Provider پیدا نشود، مقدار پیش‌فرض ارسال‌شده به createContext برگشته می‌شود. این جستجو در هر رندر انجام می‌شود، اما به لطف ممویزاسیون گره‌های fiber بسیار سریع است و بر کارایی تأثیر نمی‌گذارد.

به پیرو از React — Context internals (2024)، پیاده‌سازی داخلی useContext از لیست متصل هوک‌ها مانند useState استفاده می‌کند. هر هوک یک ارجاع به گره fiber ذخیره می‌کند که به React امکان می‌دهد سریعاً تعیین کند که کدام Provider با آن کنتکست مطابقت دارد. اگر Provider مقدار را به‌روز کند، React تمامی گره‌های fiber استفاده‌کننده از این کنتکست را برای رندر مجدد علامت‌گذاری می‌کند.

Provider و کنتکست‌های تودرو

کامپوننت‌های Provider می‌توانند در داخل یکدیگر قرار گیرند و یک سلسله‌مراتب از کنتکست‌ها ایجاد کنند. هر کامپوننت فرزند Provider مقدار والدینی را برای زیردرخت خود باطل می‌کند. این وقتی مفید است که در یک صفحه موضوع روشن و در پنجره مودال تودرو موضوع تیره نیاز است. useContext همیشه مقدار نزدیک‌ترین Provider را در بالای درخت برمی‌گرداند.

jsx
const UserContext = createContext(null);
const ThemeContext = createContext('روشن');

function App() {
    return (
        <UserContext.Provider value={{ name: 'Alice' }}>
            <ThemeContext.Provider value='dark'>
                <Profile />
            </ThemeContext.Provider>
        </UserContext.Provider>
    );
}

ایجاد پروایدر با createContext

تابع createContext(defaultValue) یک شیئ کنتکست ایجاد می‌کند. پارامتر defaultValue وقتی استفاده می‌شود که کامپوننت useContext را فراخوان کند، اما در بالای درخت Provider مناسبی وجود نداشته باشد. بدون defaultValue، useContext undefined برمی‌گرداند که می‌تواند به خطاهای غیرمنتظره منجر شود. توصیه می‌شود همیشه یک مقدار پیش‌فرض معنی‌دار یا null ارسال کنید.

ایجاد یک پروایدر سفارشی الگویی رایج برای کپسوله‌سازی منطق کنتکست است. در داخل چنین پروایدری، وضعیت (از طریق useState یا useReducer) ذخیره می‌شود و از طریق prop value در Provider در دسترس قرار می‌گیرد. این به شما امکان می‌دهد جزئیات پیاده‌سازی را از کامپوننت‌های مصرف‌کننده پنهان کرده و منطق مدیریت کنتکست را در یک مکان متمرکز کنید.

jsx
// پروایدر سفارشی با مدیریت وضعیت
const AuthContext = createContext(null);

function AuthProvider({ children }) {
    const [user, setUser] = useState(null);

    const login = useCallback(async (email, pass) => {
        const u = await loginApi(email, pass);
        setUser(u);
    }, []);

    return (
        <AuthContext.Provider value={{ user, login }}>
            {children}
        </AuthContext.Provider>
    );
}

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

در کامپوننت‌های تابعی، useContext تنها راه دسترسی به کنتکست است. آن جایگزین کامپوننت Consumer از Context API قدیمی شده است که به الگوی render-prop نیاز داشت: <ThemeContext.Consumer>{value => ...}</ThemeContext.Consumer>. useContext کد را خطی‌تر و خواناتر می‌کند، به ویژه هنگام کار با چندین کنتکست در یک کامپوننت.

هنگام استفاده از چندین کنتکست در یک کامپوننت، کافی است چند بار useContext را برای هر کنتکست فراخوان کنید. هر فراخوان مقدار Provider مربوطه را برمی‌گرداند. ترتیب فراخوان‌ها مهم نیست، چون هر کنتکست یک نهاد مستقل است. React فراخوان‌های چندگانه را از طریق همان سیستم ارجاعات fiber بهینه می‌سازد.

jsx
function Dashboard() {
    const { user } = useContext(AuthContext);
    const theme = useContext(ThemeContext);
    const { locale } = useContext(I18nContext);

    return (
        <div className={`dashboard-${theme}`}>
            <h1>{locale.greeting}, {user.name}</h1>
        </div>
    );
}

useContext در مقابل Redux: کدام را وقتی انتخاب کنیم

انتخاب بین useContext و Redux به مقیاس و پیچیدگی مدیریت وضعیت بستگی دارد. useContext + useReducer یک جایگزین سبک برای Redux برای برنامه‌های کوچک و متوسط است. نیازی به نصب کتابخانه خارجی ندارد، یادگیریش آسان‌تر است و برای اکثر کارها کافی است. Redux وقتی موجه است که معماری سختگیرانه با middleware، ابزارهای توسعه و به‌روزرسانی‌های تغییرناپذیر مورد نیاز است.

مزیت اصلی Redux بر useContext بهینه‌سازی رندرهای مجدد است. به طور پیش‌فرض، هنگام تغییر مقدار در Provider، تمامی مصرف‌کنندگان کنتکست مجدداً رندر می‌شوند. Redux با useSelector و shallowEqual به کامپوننت‌ها امکان می‌دهد فقط بر بخش‌های مشخصی از وضعیت مشترک شوند که تعداد رندرهای مجدد را در برنامه‌های بزرگ به طور قابل توجهی کاهش می‌دهد. کنتکست نیز می‌تواند از طریق تقسیم به چندین کنتکست کوچک بهینه شود.

معیارuseContextRedux
پیچیدگیوابستگی خارجی نداردنیازمند تنظیم store و middleware
رندرهای مجددتمامی مصرف‌کنندگان در هر تغییرفقط مشترک‌شوندگان به slice مشخص
DevToolsReact DevToolsRedux DevTools با time-travel
Middlewareپشتیبانی نمی‌شودRedux Thunk، Saga، Observable
وقتی انتخاب کنیمبرنامه‌های متوسط، 3-5 کنتکستبرنامه‌های بزرگ با منطق تجاری پیچیده

به پیرو از Redux maintainers — When to use Redux (2024)، 70% از برنامه‌های React به Redux نیاز ندارند. اگر کمتر از 50 کامپوننت داشته باشید و وضعیت منطق پیچیده با کش‌کردن، debounce و عوارض جانبی نداشته باشد — useContext + useReducer بیش از کافی است. Redux کدهای اضافی (boilerplate) اضافه می‌کند و باید آگاهانه استفاده شود.

اشتباهات رایج با useContext

رایج‌ترین اشتباه — ایجاد مجدد شیئ value در هر رندر Provider. اگر value={{ user, login }} را به Provider ارسال کنید، در هر رندر Provider یک شیئ جدید ایجاد می‌شود که باعث رندر مجدد تمامی مصرف‌کنندگان می‌شود، حتی اگر داده‌ها تغییر نکرده باشند. راه حل — ممویزاسیون value از طریق useMemo یا استفاده از کنتکست‌های جداگانه برای داده‌های تقیررا مکرر و کم‌تقییر.

  • رندرهای مجدد ضایع — شیئ value جدید در هر رندر Provider. برای ممویزاسیون value از useMemo استفاده کنید.
  • کنتکست خیلی بزرگ — یک Provider با دهها فیلد باعث می‌شود هر تغییر در هر فیلدی، تمامی کامپوننت‌های فرزند مجدداً رندر شوند. به چند کنتکست بر اساس منظور تقسیم کنید.
  • عدم وجود defaultValue — اگر Provider پیدا نشود، useContext defaultValue را برمی‌گرداند، و اگر undefined باشد — هر فراخوان با TypeError خطا می‌دهد.
  • Providerهای تودرو از یک نوع — باطل‌سازی کنتکست در سطوح عمیق می‌تواند گیج‌کننده باشد و به مقادیر غیرمنتظره منجر شود.
jsx
// ❌ شیئ جدید در هر رندر — تمامی مصرف‌کنندگان مجدداً رندر می‌شوند
<AuthContext.Provider value={{ user, login }}>{children}</AuthContext.Provider>

// ✅ مقدار ممویز شده — رندر مجدد فقط هنگام تغییر کاربر یا ورود
const authValue = useMemo(() => ({ user, login }), [user, login]);
<AuthContext.Provider value={authValue}>{children}</AuthContext.Provider>

برای حل مشکل «کنتکست بزرگ»، وضعیت سراسری را به گروه‌های منطقی تقسیم کنید: AuthContext، ThemeContext، I18nContext. هر کنتکست مسئول حوزه خود است و به طور مستقل به‌روز می‌شود. این کار از تلاش برای بهینه‌سازی یک کنتکست غول آور از طریق useMemo ساده‌تر است و رفتار پیش‌بینی‌پذیرتری از رندرهای مجدد ارائه می‌دهد.

سوالات متداول

آیا می‌توان کنتکست را از کامپوننت فرزند تغییر داد؟

بله، اگر یک تابع تغییردهنده به value Provider ارسال کنید. الگوی تیپیک — ذخیره‌سازی وضعیت در Provider و ارسال هم داده‌ها و هم توابع برای به‌روزرسانی آنها از طریق useContext. کامپوننت‌های فرزند این توابع را فراخوان می‌کنند و تغییر وضعیت در Provider به طور خودکار تمامی مصرف‌کنندگان را به‌روز می‌کند. این یک جایگزین اصلی برای Redux در برنامه‌های کوچک است.

کدام سریع‌تر است — useContext یا Redux؟

برای سناریوهای ساده، useContext به دلیل نبود سررسان اضافی store و middleware سریع‌تر است. اما در به‌روزرسانی‌های مکرر با تعداد زیاد مصرف‌کننده، Redux برتر است چون انتخابگرهای آن (useSelector) به بخش‌های مشخصی از وضعیت مشترک می‌شوند، در حالی که useContext تمامی مصرف‌کنندگان را در هر تغییر مجدداً رندر می‌کند. برای برنامه‌های با فراوانی بالای به‌روزرسانی (انیمیشن‌ها، real-time) انتخاب Redux یا کتابخانه‌های تخصصی توصیه می‌شود.

آیا می‌توان از useContext در خارج از کامپوننت React استفاده کرد؟

خیر. useContext، همانند تمامی هوک‌ها، فقط داخل یک کامپوننت تابعی React یا هوک سفارشی قابل فراخوان است. اگر نیاز به دستیابی به مقدار کنتکست در یک تابع عادی (مثلاً در یک ابزار یا خدمات) دارید، آن را به عنوان پارامتر از کامپوننت ارسال کنید یا از یک ماژول جداگانه با وضعیت سراسری خارج از React استفاده کنید.

useContext با TypeScript چگونه کار می‌کند؟

تایپ‌نویسی کنتکست در TypeScript — تعیین نوع در createContext است: createContext<AuthContextType | null>(null). این تضمین می‌کند که useContext(AuthContext) مقداری از نوع صحیح برمی‌گرداند. الگوی مفید — ایجاد یک هوک سفارشی useAuth که useContext را فراخوان می‌کند، null را بررسی می‌کند و خطای قابل فهمی صادر می‌کند: «useAuth must be used within AuthProvider».

چرا useContext علیرغم وجود Provider undefined برمی‌گرداند؟

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

خلاصه

  • useContext — هوکی برای خواندن مقدار از کنتکست React، بینیازکننده از props drilling.
  • createContext — شیئ کنتکست را با Provider برای انتقال داده‌ها و defaultValue برای صورت عدم وجود Provider ایجاد می‌کند.
  • ممویزاسیون value — برای جلوگیری از رندرهای مجدد ضایع از useMemo برای value در Provider استفاده کنید.
  • تقسیم کنتکست‌ها — وضعیت سراسری را به چند کنتکست کوچک بر اساس گروه‌های منطقی تقسیم کنید.
  • سلسله‌مراتب Provider — می‌توان Provider همین نوع را برای باطل‌سازی مقدار در بخشی از درخت تودرو قرار داد.
  • useContext + useReducer — جایگزین سبک Redux برای برنامه‌های متوسط بدون وابستگی‌های خارجی.
  • جایگزین Redux نیست — برای منطق پیچیده با middleware و به‌روزرسانی‌های مکرر Redux را با انتخابگرها انتخاب کنید.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید