غیر متعلقہ کمپوننٹس کی تہوں کے ذریعے props پاس کرنا ایک تھکا دینے والا کام ہے۔ ایک دن آپ کوئی فیچر لانچ کر رہے ہوتے ہیں، اور اگلے ہی دن صرف ایک prop کا نام بدلنے کے لیے آپ کو چھ فائلیں ایڈٹ کرنی پڑتی ہیں۔ یہی "prop drilling" کا خلاصہ ہے: ایک پیرنٹ کے پاس ڈیٹا ہوتا ہے، ایک گہرے چائلڈ کو اس کی ضرورت ہوتی ہے، اور ان کے درمیان موجود ہر کمپوننٹ ایک کورئیر بن جاتا ہے۔ ایپ تو چلتی رہتی ہے، لیکن کوڈ بیس نازک ہو جاتا ہے۔ اگر آپ درمیان کی کوئی تہہ ہٹا دیں، تو آدھا ٹری (tree) گر جاتا ہے۔ اگر آپ ٹائپ (type) تبدیل کریں، تو TypeScript تین مختلف ڈائریکٹریز میں شکایت کرنے لگتا ہے۔ React Context API ان درمیانی واسطوں کو مکمل طور پر ختم کرنے کے لیے موجود ہے۔

Prop Drilling اصل میں کیسی دکھتی ہے

ایک عام ایپ شیل کا تصور کریں۔ آپ کے پاس ایک App کمپوننٹ ہے جو موجودہ صارف (user) کا ڈیٹا حاصل کرتا ہے۔ App کے اندر ایک Layout ہے، Layout کے اندر ایک Sidebar ہے، Sidebar کے اندر ایک Navigation ہے، اور آخر کار Navigation کے اندر آپ کو UserAvatar ملتا ہے جسے اصل میں user object کی ضرورت ہوتی ہے۔

آپ کا کوڈ کچھ اس طرح نظر آتا ہے:

function App() {
  const user = { name: 'Aarav', role: 'admin' };
  return <Layout user={user} />;
}

function Layout({ user }) {
  return <Sidebar user={user} />;
}

function Sidebar({ user }) {
  return <Navigation user={user} />;
}

function Navigation({ user }) {
  return <UserAvatar user={user} />;
}

Layout، Sidebar اور Navigation اس user object کے ساتھ کچھ نہیں کرتے سوائے اسے آگے بھیجنے کے۔ وہ ایسے props جمع کرتے رہتے ہیں جن کے وہ مالک نہیں ہوتے، ان کے انٹرفیس (interfaces) بڑھتے جاتے ہیں، اور انہیں ٹیسٹ کرنے کے لیے ایسے ڈیٹا کو موک (mock) کرنا پڑتا ہے جسے وہ کبھی چھوتے بھی نہیں۔ اصل جرم یہ ہے کہ یہ کتنی تیزی سے پھیلتا ہے۔ ایک isLoggedIn فلیگ، ایک locale اسٹرنگ، یا ایک theme ویلیو شامل کریں، اور وہی عمل بار بار دہرایا جاتا ہے۔

Context API کھیل کو کیسے بدل دیتی ہے

Context API کو ایک WiFi راؤٹر کی طرح سمجھیں۔ ہر ڈیوائس تک پہنچنے کے لیے ہر کمرے میں لمبی کیبلز بچھانے کے بجائے، راؤٹر ہوا کے ذریعے سگنل بھیجتا ہے۔ رینج میں موجود کوئی بھی ڈیوائس براہ راست کنیکٹ ہو سکتی ہے۔ React کی اصطلاح میں، راؤٹر "Provider" ہے، سگنل آپ کا state یا ڈیٹا ہے، اور ڈیوائس کوئی بھی نیস্টেڈ (nested) کمپوننٹ ہے جو useContext کو کال کرتا ہے۔

اس کے سیٹ اپ کے تین اہم حصے ہیں:

  • React.createContext() ڈیٹا چینل بناتا ہے۔
  • Provider آپ کے ٹری کے ایک حصے کو لپیٹتا (wrap کرتا) ہے اور ایک ویلیو منتقل کرتا ہے۔
  • useContext ہوک descendants کو درمیانی props کو چھوئے بغیر وہ ویلیو حاصل کرنے کی اجازت دیتا ہے۔

آپ کے پاس اب بھی ایک ٹری ہے، لیکن روٹ (root) اور پتے (leaf) کے درمیان شاخوں کو اب کسی کورئیر معاہدے کی ضرورت نہیں ہے۔

شروع سے Context بنانا

آئیے تھیم سیٹنگز کے ساتھ ایک ٹھوس مثال بناتے ہیں، کیونکہ زیادہ تر ایپس کو کسی نہ کسی وقت لائٹ یا ڈارک موڈ کی ضرورت ہوتی ہے۔

سب سے پہلے، context آبجیکٹ بنائیں۔ یہ ایک پائپ کی طرح ہے:

import { createContext, useState, useMemo } from 'react';

const ThemeContext = createContext(null);

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

export default ThemeContext;

پھر اپنی ایپلی کیشن کو provider میں لپیٹ دیں۔ عام طور پر یہ روٹ کے قریب ہوتا ہے:

import { ThemeProvider } from './ThemeContext';

function App() {
  return (
    <ThemeProvider>
      <Layout />
    </ThemeProvider>
  );
}

اب کوئی بھی descendant براہ راست سگنل حاصل کر سکتا ہے۔ یہاں UI کے اندر گہرائی میں چھپا ہوا ایک ٹوگل بٹن ہے:

import { useContext } from 'react';
import ThemeContext from './ThemeContext';

function ThemeToggle() {
  const { theme, setTheme } = useContext(ThemeContext);

  return (
    <button
      onClick={() => setTheme(prev => prev === 'light' ? 'dark' : 'light')}
    >
      Current theme: {theme}
    </button>
  );
}

غور کریں کہ Layout، Sidebar اور Navigation نے کبھی بھی theme prop کو نہیں دیکھا۔ وہ معمول کے مطابق رینڈر ہوتے ہیں، اور ThemeToggle براہ راست context سے وہ چیز حاصل کر لیتا ہے جس کی اسے ضرورت ہوتی ہے۔ اس کی وائرنگ باہر سے نظر نہیں آتی، اور یہی اصل مقصد ہے۔

Context اصل میں کہاں فٹ بیٹھتا ہے

Context اس ڈیٹا کے لیے بہترین کام کرتا ہے جسے بہت سے دور دراز کمپوننٹس شیئر کرتے ہیں لیکن کوئی ایک پیرنٹ اسے مکمل طور پر کنٹرول نہیں کرتا۔ اچھے امیدواروں میں شامل ہیں:

  • Theme settings جیسے لائٹ یا ڈارک موڈ، accent colors، یا فونٹ اسکیلنگ۔
  • Authentication state جیسے موجودہ user object، لاگ ان اسٹیٹس، یا سیشن کی میعاد ختم ہونا۔
  • Language and locale سیٹنگز بین الاقوامی ضروریات (internationalization) کے لیے۔
  • Shopping cart data جسے ہیڈر بیج، منی کارٹ ڈراپ ڈاؤن، اور چیک آؤٹ پیج پر ہم آہنگ (sync) رہنا چاہیے۔

ہر مقامی اسٹیٹ (local state) کو Context میں ڈالنے کی خواہش سے بچیں۔ دو لیول نیچے موجود فارم ان پٹ کو عالمی نشریات (global broadcast) کی ضرورت نہیں ہے۔ Context کو حقیقی cross-cutting concerns کے لیے رکھیں، اور باقی چیزوں کو عام props رہنے دیں۔

پرفارمنس کے جال اور ان سے بچنے کا طریقہ

Context مفت نہیں ملتا۔ جب context کی ویلیو اپ ڈیٹ ہوتی ہے، تو اس context سے جڑا ہوا ہر کمپوننٹ دوبارہ رینڈر (re-render) ہوتا ہے، چاہے وہ ویلیو کا وہ حصہ نہ بدلا ہو جس کی انہیں پرواہ ہے۔ کلاسک غلطی ہر پیرنٹ رینڈر پر Provider میں ایک نیا آبجیکٹ لٹرل (object literal) ڈالنا ہے۔

ہماری تھیم کی مثال میں، جب بھی ThemeProvider دوبارہ رینڈر ہوتا ہے کیونکہ اس کا اپنا پیرنٹ اپ ڈیٹ ہوا ہے، تو { theme, setTheme } کا ایکسپریشن ایک بالکل نیا آبجیکٹ تخلیق کرتا ہے۔ React ایک نیا ریفرنس دیکھتا ہے، اور ہر صارف (consumer) اپ ڈیٹ ہو جاتا ہے۔ اگر آپ کا تھیم شاذ و نادر ہی بدلتا ہے لیکن آپ کی ایپ کا اسٹیٹ اکثر بدلتا ہے، تو آپ ان رینڈرز کی قیمت ادا کر رہے ہیں جن کی آپ کو ضرورت نہیں ہے۔

اس کا حل دوہرا ہے۔

اپنے contexts کو اپ ڈیٹ کی فریکوئنسی کے لحاظ سے تقسیم کریں۔ ایک UserContext جو لاگ ان کے دوران صرف ایک بار بدلتا ہے، اسے NotificationContext کے ساتھ شیئر نہیں کرنا چاہیے جو ہر چند سیکنڈ بعد اپ ڈیٹ ہوتا ہے۔ انہیں الگ رکھیں تاکہ اسٹیٹک ڈیٹا اسی ری-رینڈر ٹرین پر نہ چلے جس پر اتار چڑھاؤ والا (volatile) ڈیٹا چل رہا ہو۔

جب ویلیو ایک آبجیکٹ یا ایرے ہو تو اسے useMemo میں لپیٹ دیں۔ React کو ایک مستحکم ریفرنس دیں:

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  const value = useMemo(() => ({ theme, setTheme }), [theme]);

  return (
    <ThemeContext.Provider value={value}>
      {children}
    </ThemeContext.Provider>
  );
}

Now the object identity only shifts when theme actually changes. Descendant components that care about the context but are shielded by React.memo further down will skip the work.

Mistakes That Waste Your Time

The two errors that still show up in production code are easy to prevent.

First, forgetting to export the context itself. If you only export the Provider wrapper and keep the context object private, a developer writing a new feature cannot call useContext without refactoring your module. Export the context so consumers can import both the provider and the consumer hook cleanly.

Second, calling useContext outside the corresponding Provider. If ThemeToggle renders in a branch of the tree that is not wrapped in ThemeProvider, the hook returns the default value passed to createContext, or undefined if you passed none. This leads to silent failures like cannot read property of undefined. You can guard against this by assigning a sensible default or throwing a clear error early in the hook call.

Context vs Redux: Keep It Simple

You do not always need Redux. For medium-sized projects, Context paired with useState or useReducer covers the bulk of state sharing. Redux shines when you need time-travel debugging, complex middleware, or global transactions that must roll back in sequence. If your entire state story is a user object, a theme string, and a cart array, a store library adds boilerplate you will never leverage.

That said, Context is not a full state management system on its own. It does not give you a single global snapshot, and it does not batch updates across unrelated contexts. Use it as a replacement for prop drilling, not as an operating system for your entire data layer.

The Real Takeaway

Stop threading props through components that do not care about them. Create focused contexts for the data that actually spans your tree, wrap providers high enough to cover the consumers, and always stabilize the value object when you are passing collections or functions. Context API keeps yourReact code direct: props stay local, global data travels wirelessly, and your component boundaries stay clean.