useContext — یک هوک React است که به کامپوننتهای تابعی دسترسی مستقیم به دادههای کنتکستی که از طریق createContext ایجاد شده است فراهم میکند. کنتکست در React مشکل props drilling را حل میکند — انتقال props از طریق چندین کامپوننت واسطه که خود از این دادهها استفاده نمیکنند. به پیرو از React Documentation (2025)، useContext شیئ کنتکست را میپذیرد و مقدار جاری تعیینشده توسط نزدیکترین Provider در بالای درخت کامپوننتها را برمیگرداند. هنگامی که مقدار در Provider تغییر میکند، تمامی کامپوننتهایی که از useContext استفاده میکنند بهطور خودکار مجدداً رندر میشوند.
نکات کلیدی
useContext — هوکی است که در React 16.8 همراه با سایر هوکها اضافه شد و به شما امکان خواندن مقدار از کنتکست React را میدهد. کنتکست یک مکانیسم داخلی در React است که برای انتقال دادهها از طریق درخت کامپوننتها بدون نیاز به انتقال دستی props در هر سطح طراحی شده است. useContext جایگزین کامپوننت Consumer از Context API قدیمی شده و کد را موجزتر و خواناتر میکند.
سناریوهای تیپیک استفاده از کنتکست شامل موضوعهای ظاهری (روشن/تیره)، محلیسازی و ترجمه (i18n)، احراز هویت کاربر، تنظیمات برنامه و سایر دادههای سراسری که در سطوح مختلف تودرو به کامپوننتهای متعدد نیاز دارند. تیم React توصیه میکند از کنتکست برای دادههایی استفاده کنید که برای زیردرخت کامپوننتها سراسری هستند، اما نه برای کل برنامه.
به پیرو از React Team — Context documentation (2025)، استفاده نادرست از کنتکست یکی از علل اصلی مشکلات کارایی در برنامههای React است. هر تغییر مقدار در Provider باعث رندر مجدد تمامی مصرفکنندگان میشود، بدون توجه به اینکه کدام بخش از دادهها تغییر کرده است. بهینهسازی از طریق useMemo و تقسیم کنتکستها این مشکل را حل میکند.
import { createContext, useContext } from 'react';
// ایجاد کنتکست با مقدار پیشفرض
const ThemeContext = createContext('روشن');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button className={`btn-${theme}`}>Click</button>;
}
مکانیسم کنتکست در 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 مقدار والدینی را برای زیردرخت خود باطل میکند. این وقتی مفید است که در یک صفحه موضوع روشن و در پنجره مودال تودرو موضوع تیره نیاز است. useContext همیشه مقدار نزدیکترین Provider را در بالای درخت برمیگرداند.
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(defaultValue) یک شیئ کنتکست ایجاد میکند. پارامتر defaultValue وقتی استفاده میشود که کامپوننت useContext را فراخوان کند، اما در بالای درخت Provider مناسبی وجود نداشته باشد. بدون defaultValue، useContext undefined برمیگرداند که میتواند به خطاهای غیرمنتظره منجر شود. توصیه میشود همیشه یک مقدار پیشفرض معنیدار یا null ارسال کنید.
ایجاد یک پروایدر سفارشی الگویی رایج برای کپسولهسازی منطق کنتکست است. در داخل چنین پروایدری، وضعیت (از طریق useState یا useReducer) ذخیره میشود و از طریق prop value در Provider در دسترس قرار میگیرد. این به شما امکان میدهد جزئیات پیادهسازی را از کامپوننتهای مصرفکننده پنهان کرده و منطق مدیریت کنتکست را در یک مکان متمرکز کنید.
// پروایدر سفارشی با مدیریت وضعیت
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 تنها راه دسترسی به کنتکست است. آن جایگزین کامپوننت Consumer از Context API قدیمی شده است که به الگوی render-prop نیاز داشت: <ThemeContext.Consumer>{value => ...}</ThemeContext.Consumer>. useContext کد را خطیتر و خواناتر میکند، به ویژه هنگام کار با چندین کنتکست در یک کامپوننت.
هنگام استفاده از چندین کنتکست در یک کامپوننت، کافی است چند بار useContext را برای هر کنتکست فراخوان کنید. هر فراخوان مقدار Provider مربوطه را برمیگرداند. ترتیب فراخوانها مهم نیست، چون هر کنتکست یک نهاد مستقل است. React فراخوانهای چندگانه را از طریق همان سیستم ارجاعات fiber بهینه میسازد.
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 + useReducer یک جایگزین سبک برای Redux برای برنامههای کوچک و متوسط است. نیازی به نصب کتابخانه خارجی ندارد، یادگیریش آسانتر است و برای اکثر کارها کافی است. Redux وقتی موجه است که معماری سختگیرانه با middleware، ابزارهای توسعه و بهروزرسانیهای تغییرناپذیر مورد نیاز است.
مزیت اصلی Redux بر useContext بهینهسازی رندرهای مجدد است. به طور پیشفرض، هنگام تغییر مقدار در Provider، تمامی مصرفکنندگان کنتکست مجدداً رندر میشوند. Redux با useSelector و shallowEqual به کامپوننتها امکان میدهد فقط بر بخشهای مشخصی از وضعیت مشترک شوند که تعداد رندرهای مجدد را در برنامههای بزرگ به طور قابل توجهی کاهش میدهد. کنتکست نیز میتواند از طریق تقسیم به چندین کنتکست کوچک بهینه شود.
| معیار | useContext | Redux |
|---|---|---|
| پیچیدگی | وابستگی خارجی ندارد | نیازمند تنظیم store و middleware |
| رندرهای مجدد | تمامی مصرفکنندگان در هر تغییر | فقط مشترکشوندگان به slice مشخص |
| DevTools | React DevTools | Redux 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) اضافه میکند و باید آگاهانه استفاده شود.
رایجترین اشتباه — ایجاد مجدد شیئ value در هر رندر Provider. اگر value={{ user, login }} را به Provider ارسال کنید، در هر رندر Provider یک شیئ جدید ایجاد میشود که باعث رندر مجدد تمامی مصرفکنندگان میشود، حتی اگر دادهها تغییر نکرده باشند. راه حل — ممویزاسیون value از طریق useMemo یا استفاده از کنتکستهای جداگانه برای دادههای تقیررا مکرر و کمتقییر.
// ❌ شیئ جدید در هر رندر — تمامی مصرفکنندگان مجدداً رندر میشوند
<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 به دلیل نبود سررسان اضافی store و middleware سریعتر است. اما در بهروزرسانیهای مکرر با تعداد زیاد مصرفکننده، Redux برتر است چون انتخابگرهای آن (useSelector) به بخشهای مشخصی از وضعیت مشترک میشوند، در حالی که useContext تمامی مصرفکنندگان را در هر تغییر مجدداً رندر میکند. برای برنامههای با فراوانی بالای بهروزرسانی (انیمیشنها، real-time) انتخاب Redux یا کتابخانههای تخصصی توصیه میشود.
خیر. useContext، همانند تمامی هوکها، فقط داخل یک کامپوننت تابعی React یا هوک سفارشی قابل فراخوان است. اگر نیاز به دستیابی به مقدار کنتکست در یک تابع عادی (مثلاً در یک ابزار یا خدمات) دارید، آن را به عنوان پارامتر از کامپوننت ارسال کنید یا از یک ماژول جداگانه با وضعیت سراسری خارج از React استفاده کنید.
تایپنویسی کنتکست در TypeScript — تعیین نوع در createContext است: createContext<AuthContextType | null>(null). این تضمین میکند که useContext(AuthContext) مقداری از نوع صحیح برمیگرداند. الگوی مفید — ایجاد یک هوک سفارشی useAuth که useContext را فراخوان میکند، null را بررسی میکند و خطای قابل فهمی صادر میکند: «useAuth must be used within AuthProvider».
شایعترین علت — کامپوننت مصرفکننده در خارج از Provider مربوطه قرار دارد. بررسی کنید که Provider تمام زیردرختی که useContext در آن استفاده شده را پوشش میدهد. علت دوم — ارسال Provider به شیئ کنتکستی دیگر: توسعهدهنده با فراخوانی createContext کنتکست را ایجاد میکند، اما از useContext با یک نمونه دیگر از createContext استفاده میکند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید