ہر React ڈویلپر کو بالآخر ایک ہی دیوار کا سامنا کرنا پڑتا ہے۔ آپ اپنے ٹاپ لیول App کمپوننٹ کے اندر ایک یوزر آبجیکٹ (user object) فیچ کرتے ہیں۔ پھر اسے نیچے پاس کرتے ہیں۔ اور پھر مزید نیچے پچھلے کمپوننٹس کے ذریعے؛ ایک روٹ ریپر (route wrapper)، ایک لے آؤٹ شیل (layout shell)، اور ایک سائیڈ بار کنٹینر کے ذریعے، صرف اس لیے کہ تین لیئرز گہرائی میں موجود ایک چھوٹا سا ایواٹار (avatar) کمپوننٹ پروفائل پکچر دکھا سکے۔ درمیان والے کمپوننٹس کو اس یوزر آبجیکٹ سے کوئی سروکار نہیں ہوتا۔ وہ صرف ایک پارسل آگے پہنچا رہے ہوتے ہیں۔ اسے "prop drilling" کہتے ہیں، اور یہ ایک صاف ستھرے کمپوننٹ ٹری کو ٹیلیفون کے ایک پریشان کن کھیل میں بدل دیتا ہے۔
اصل تکلیف تب شروع ہوتی ہے جب اس ڈیٹا کی شکل (shape) تبدیل ہوتی ہے۔ شاید بیک اینڈ user.avatar کے بجائے user.profile.avatar کو نےسٹ (nest) کرنا شروع کر دے۔ اچانک آپ کو ان پانچ فائلوں میں TypeScript انٹرفیسز یا PropTypes کو ایڈٹ کرنا پڑتا ہے جو خود کبھی اس ڈیٹا کو استعمال نہیں کرتیں۔ یہیں پر React Context API کام آتی ہے۔
Context ڈیٹا کے بہاؤ کو کیسے تبدیل کرتا ہے
Context کو اپنے گھر کے مرکز میں موجود ایک WiFi راؤٹر کے طور پر سوچیں۔ اس کے بغیر، آپ کو اپنے لیپ ٹاپ تک سگنل پہنچانے کے لیے ہر کمرے میں ایتھرنیٹ کیبلز (Ethernet cables) بچھانی پڑیں گی۔ اس کے ساتھ، راؤٹر ہوا کے ذریعے سگنل پھیلاتا ہے، اور صحیح پاس ورڈ والا کوئی بھی آلہ براہ راست کنیکٹ ہو سکتا ہے۔ دیواریں کوئی معنی نہیں رکھتیں۔
React کی اصطلاح میں، آپ کی ایپ کا روٹ (root) ہر لیئر کو کورئیر (courier) کے طور پر کام کرنے کے لیے کہے بغیر، کمپوننٹ ٹری کے ذریعے ڈیٹا براڈکاسٹ کر سکتا ہے۔ کوئی بھی نیسٹڈ (nested) کمپوننٹ اس براڈکاسٹ کو سبسکرائب کر سکتا ہے اور بالکل وہی حاصل کر سکتا ہے جس کی اسے ضرورت ہے۔
تین بنیادی حصے
Context API تین متحرک حصوں پر مشتمل ہے۔
React.createContext() براڈکاسٹ چینل سیٹ اپ کرتا ہے۔ یہ ایک آبجیکٹ واپس کرتا ہے جس میں ایک Provider اور (پرانے کوڈ میں) ایک Consumer ہوتا ہے۔ آپ کو کسی مخصوص فیچر کے لیے اسے صرف ایک بار کال کرنے کی ضرورت ہوتی ہے۔
The Provider ایک کمپوننٹ ہے جو آپ کے ٹری کے ایک حصے کو لپیٹتا (wrap کرتا) ہے۔ یہ value نامی ایک پروپ (prop) قبول کرتا ہے۔ آپ اس پروپ میں جو کچھ بھی رکھتے ہیں وہ ہر اس کمپوننٹ کے لیے دستیاب ہو جاتا ہے جو اس کے نیچے ہے، چاہے وہ کتنی ہی گہرائی میں کیوں نہ ہو۔
useContext وہ Hook ہے جو ایک فنکشن کمپوننٹ کو اس براڈکاسٹ سے منسلک ہونے کی اجازت دیتا ہے۔ اپنے کمپوننٹ کے اندر، آپ اپنے بنائے ہوئے context آبجیکٹ کو useContext میں پاس کرتے ہیں، اور یہ موجودہ ویلیو واپس کرتا ہے۔ بس اتنا ہی۔ کوئی ریپرز (wrappers) نہیں، کوئی اضافی پروپس نہیں۔
Hooks کے آنے سے پہلے، آپ کو render props کے ساتھ Consumer پیٹرن استعمال کرنا پڑتا تھا۔ یہ کام تو کرتا تھا، لیکن اس سے انڈینٹیشن (indentation) اور ریپرز کا بہت زیادہ ڈھیر لگ جاتا تھا۔ useContext نے اس سب کو آپ کے فنکشن باڈی کے اندر ایک ہی لائن میں سمٹا دیا۔
Context کب واقعی کارآمد ہے
صرف عادت کی وجہ سے Context کا استعمال نہ کریں۔ یہ اس ڈیٹا کے لیے بنایا گیا ہے جسے آپ کے ٹری کی مختلف شاخوں میں بہت سے غیر متعلقہ کمپوننٹس شیئر کرتے ہیں۔ اچھے امیدواروں میں شامل ہیں:
- Theme settings (تھیم سیٹنگز): صرف لائٹ یا ڈارک موڈ ہی نہیں، بلکہ اسپیسنگ ٹوکنز (spacing tokens)، کلر پیلیٹس، اور فونٹ اسکیلز۔ ان سب کو ہر اسٹائلڈ بٹن اور ماڈل کے ذریعے دستی طور پر گزارنا جلد ہی اکتا دینے والا کام بن جاتا ہے۔
- User authentication (یوزر آتھنٹیکیشن): لاگ ان اسٹیٹس، پرمیشنز کا ایرے (permissions array)، یا موجودہ یوزر آبجیکٹ۔ آپ کا ہیڈر بار، ایک ڈیش بورڈ ویجٹ، اور ایک پرائیویٹ روٹ گارڈ، یہ سب ٹری کے مختلف کونوں میں ہو سکتے ہیں۔
- Language preferences (زبان کی ترجیحات): لوکیل اسٹرنگز (locale strings)، تاریخ کے فارمیٹس، اور کرنسی کے نشانات۔ فارم لیبلز جیسے گہرے لیف کمپوننٹس (leaf components) کو ان کی ضرورت ہوتی ہے بغیر اس کے کہ راستے میں موجود ہر پیرنٹ کو ان کے بارے میں معلوم ہو۔
- Shopping cart data (شاپنگ کارٹ ڈیٹا): آئٹم کی تعداد، کل قیمت، اور add-to-cart فنکشنز۔ ہیڈر بیج (header badge) اور چیک آؤٹ پیج کو ایک ہی اسٹیٹ کی ضرورت ہوتی ہے، لیکن وہ عام طور پر بالکل مختلف لے آؤٹ شاخوں کے نیچے ہوتے ہیں۔
ایک عملی تھیم سوئچر
Context کو عملی طور پر دیکھنے کا ایک واضح طریقہ تھیم ٹوگل (theme toggle) ہے۔ یہاں بتایا گیا ہے کہ آپ اہم تفصیلات کو چھوڑے بغیر اسے کیسے ترتیب دے سکتے ہیں۔
پہلے، ایک ThemeContext.js فائل بنائیں۔ React.createContext() کو کال کریں اور نتیجہ اسٹور کریں۔ پھر ایک ThemeProvider کمپوننٹ بنائیں جو useState یا useReducer کے ساتھ موجودہ تھیم کو مینیج کرے۔ بچوں (children) کو اپنے کنٹیکسٹ کے Provider میں لپیٹیں، اور ایک ایسا آبجیکٹ پاس کریں جس میں موجودہ تھیم اور اسے ٹوگل کرنے کا فنکشن دونوں شامل ہوں۔ ThemeProvider اور خود کنٹیکسٹ آبجیکٹ دونوں کو ایکسپورٹ کریں۔
دوسرا، اپنی ایپ کے انٹری پوائنٹ (entry point) پر جائیں۔ ThemeProvider کو امپورٹ کریں اور اپنی پوری ایپلی کیشن کو اس کے ساتھ ریپ کریں۔ اگر آپ یہ قدم چھوڑ دیتے ہیں، تو بعد میں کنٹیکسٹ کو پڑھنے کی کوشش کرنے والی کوئی بھی چیز صرف ڈیفالٹ ویلیو ہی دیکھ سکے گی۔
تیسرا، کسی Header یا Content کمپوننٹ کے اندر، کنٹیکسٹ آبجیکٹ اور useContext کو امپورٹ کریں۔ Hook کو کال کریں، تھیم اور ٹوگل فنکشن کو ڈیسٹرکچر (destructure) کریں، اور اپنی CSS کلاسز کو شرط کے مطابق (conditionally) لاگو کریں۔ ایک بٹن شامل کریں جو ٹوگل کو کال کرے۔ کمپوننٹ کو اپنے پیرنٹ سے کبھی بھی theme پروپ موصول نہیں ہوتا۔ یہ سگنل براہ راست ہوا سے حاصل کرتا ہے۔
Prop Drilling، Context، یا Redux؟
ان ٹولز کے درمیان انتخاب وفاداری کے بارے میں کم اور آپ کے اسٹیٹ (state) کی ساخت کے بارے میں زیادہ ہے۔
Prop drilling دو یا تین لیولز کی گہرائی کے لیے بالکل ٹھیک ہے۔ یہ واضح ہے، آپ کے IDE میں اسے ٹریس کرنا آسان ہے، اور یہ ڈیپینڈنسیز (dependencies) کو نمایاں رکھتا ہے۔ مسائل تب ہی سامنے آتے ہیں جب آپ ایک ہی prop کو چھ یا سات لیئرز کے ذریعے گزارنا شروع کرتے ہیں۔
Context API خود React کے ساتھ آتا ہے۔ اس کا مطلب ہے کہ کوئی اضافی بنڈل سائز نہیں اور کوئی بیرونی سیٹ اپ نہیں۔ یہ چھوٹے سے درمیانے درجے کے گلوبل اسٹیٹ کو بہترین طریقے سے سنبھالتا ہے، خاص طور پر وہ ڈیٹا جو کم ہی تبدیل ہوتا ہے جیسے تھیمز (themes) یا یوزر پروفائلز۔
Redux کے لیے اضافی لائبریریز انسٹال کرنے اور بوائلر پلیٹ (boilerplate) لکھنے کی ضرورت ہوتی ہے۔ یہ اس وقت فائدہ مند ثابت ہوتا ہے جب آپ کے اسٹیٹ کا لاجک پیچیدہ ہو، جب اسٹیٹ کے متعدد حصے (slices) گہرے طریقوں سے ایک دوسرے کے ساتھ تعامل کریں، یا جب آپ کو time-travel debugging اور middleware کی ضرورت ہو۔ سادہ گلوبل ڈیٹا کے لیے، Redux ضرورت سے زیادہ (overkill) ہے۔
کارکردگی کی وہ حقیقت جس پر کوئی بات نہیں کرتا
یہاں وہ اہم نکتہ ہے جو جونیئر اور سینئر امپلیمنٹیشنز کے درمیان فرق کرتا ہے۔ جب Context Provider کی ویلیو تبدیل ہوتی ہے، تو اس کانٹیکسٹ کو استعمال کرنے والا ہر کمپوننٹ دوبارہ رینڈر (re-render) ہوتا ہے۔ اس سے کوئی فرق نہیں پڑتا کہ وہ مخصوص حصہ (slice) جس کی اس کمپوننٹ کو ضرورت تھی، وہ تبدیل ہوا یا نہیں۔ React نئی ریفرنس (reference) کو دیکھتا ہے اور اپ ڈیٹ کا شیڈول بنا دیتا ہے۔
اگر آپ اپنی پوری ایپلی کیشن کا اسٹیٹ ایک ہی بڑے StoreContext میں ڈال دیتے ہیں، تو آپ نے عملی طور پر اپنے پورے UI کو ایک دوسرے کے ساتھ چپکا دیا ہے۔ تھیم کی سیٹنگ تبدیل کرنے سے آپ کا شاپنگ کارٹ، ڈیش بورڈ چارٹس، اور نوٹیفکیشن لسٹ دوبارہ رینڈر ہوں گے۔ یہ ایک غیر ضروری کام ہے۔
اپنے کانٹیکسٹ کو ڈومین (domain) کے لحاظ سے تقسیم کریں۔ بصری سیٹنگز کے لیے ThemeContext رکھیں، پروفائل ڈیٹا کے لیے UserContext رکھیں، اور کامرس اسٹیٹ کے لیے CartContext رکھیں۔ اگر کوئی صارف اپنا ڈسپلے نیم تبدیل کرتا ہے، تو آپ کا ہیڈر پروڈکٹ گرڈ کو چھوئے بغیر اپ ڈیٹ ہو جائے گا۔ اس کے علاوہ، اس بات کا خیال رکھیں کہ آپ Provider کے value prop میں کیا پاس کر رہے ہیں۔ اگر آپ رینڈر کے دوران ان لائن ایک آبجیکٹ لٹرل { theme, toggleTheme } پاس کرتے ہیں، تو آپ ہر رینڈر پر ایک نئی ریفرنس بناتے ہیں اور غیر ضروری اپ ڈیٹس کا سبب بنتے ہیں۔ اگر ویلیو میں فنکشنز یا نان پرائمٹیو (non-primitive) ڈیٹا شامل ہے، تو useMemo کے ذریعے اس ساخت کو مستحکم کریں۔
وہ غلطیاں جو گھنٹوں ضائع کرتی ہیں
دو ایسی غلطیاں ہیں جو ٹیموں کو بار بار پکڑتی ہیں۔
کانٹیکسٹ آبجیکٹ کو ایکسپورٹ کرنا بھول جانا۔ یہ آسان ہے کہ آپ ThemeProvider کمپوننٹ کو ایکسپورٹ کریں اور پھر useContext(ThemeProvider) کو کال کرنے کی کوشش کریں۔ یہ اس طرح کام نہیں کرتا۔ Hook کو createContext کے ذریعے واپس آنے والے کانٹیکسٹ آبجیکٹ کی ضرورت ہوتی ہے، نہ کہ ریپر (wrapper) کمپوننٹ کی۔ اگر آپ صرف Provider کو ایکسپورٹ کرتے ہیں، تو آپ کے کنزیومرز (consumers) کے پاس امپورٹ کرنے کے لیے کچھ نہیں ہوگا۔
useContext کو اس کے Provider سے باہر کال کرنا۔ Hook وہ ڈیفالٹ ویلیو واپس کرتا ہے جو آپ نے createContext میں پاس کی تھی۔ اگر آپ نے کوئی ڈیفالٹ پاس نہیں کی، تو آپ کو undefined ملے گا۔ اگر آپ کا کمپوننٹ ٹری کنزیومر کو Provider کے مقابلے میں DOM میں اوپر رینڈر کرتا ہے، یا اگر Provider بالکل ہی موجود نہیں ہے، تو آپ کا ڈیٹا وصول نہیں ہوگا۔ اس بات کی دوبارہ تصدیق کریں کہ آپ کی index یا root فائل واقعی ایپ کو ریپ (wrap) کر رہی ہے۔
اصل خلاصہ
React Context اسٹیٹ مینجمنٹ میں کوئی انقلاب نہیں ہے۔ یہ ایک مخصوص جگہ کے مسئلے کے لیے ایک ہدف شدہ ٹول ہے: ڈیٹا کو دور دراز کے کمپوننٹس تک پہنچانا بغیر ہر لیئر کو پوسٹ آفس بنائے رکھے۔ اسے حقیقی طور پر گلوبل ڈیٹا کے لیے استعمال کریں، رینڈرنگ کارکردگی کو برقرار رکھنے کے لیے اپنے کانٹیکسٹ کو ڈومین کے لحاظ سے تقسیم رکھیں، اور سگنل پڑھنے کی کوشش کرنے سے پہلے ہمیشہ اپنے ٹری کو درست Provider کے ساتھ ریپ کریں۔ ان عادات کو اپنائیں، اور آپ کے کمپوننٹ ٹریز صاف، تیز، اور سمجھنے میں آسان رہیں گے۔
