प्रत्येक React डेव्हलपरला शेवटी एकाच अडचणीचा सामना करावा लागतो. तुम्ही तुमच्या टॉप-लेव्हल App कंपोनंटमध्ये एक युजर ऑब्जेक्ट फेच करता. मग तो खाली पास करता. आणि पुन्हा खाली. एका राऊट रॅपरमधून, एका लेआउट शेलमधून, एका साइडबार कंटेनरमधून, जेणेकरून तीन लेयर्स खोलवर असलेला एक छोटा अवतार कंपोनंट प्रोफाईल पिक्चर दाखवू शकेल. मधले कंपोनंट्सना त्या युजर ऑब्जेक्टशी काहीही देणेघेणे नसते. ते फक्त तो पार्सल पुढे पाठवत असतात. यालाच 'प्रॉप ड्रिलिंग' (prop drilling) म्हणतात, आणि यामुळे एक स्वच्छ कंपोनंट ट्री एका त्रासदायक 'टेलिफोन गेम'मध्ये रूपांतरित होते.
खरा त्रास तेव्हा सुरू होतो जेव्हा त्या डेटाचे स्वरूप बदलते. कदाचित बॅकएंड user.avatar ऐवजी user.profile.avatar असे नेस्टेड डेटा पाठवू लागते. अचानक तुम्हाला अशा पाच फाईल्समध्ये TypeScript इंटरफेसेस किंवा PropTypes बदलावे लागतात, ज्या फाईल्स स्वतः कधीही त्या डेटाचा वापर करत नाहीत. इथेच React Context API मदतीला येते.
Context डेटा फ्लो कसा बदलतो
Context ला तुमच्या घराच्या मध्यभागी असलेला एक वाय-फाय (WiFi) राउटर समजा. त्याशिवाय, तुमच्या लॅपटॉपला सिग्नल मिळवण्यासाठी तुम्हाला प्रत्येक खोलीतून इथरनेट केबल्स वळणावळणाने न्याव्या लागतील. राउटरमुळे, तो हवेतून ब्रॉडकास्ट करतो आणि योग्य पासवर्ड असलेला कोणताही डिव्हाइस थेट कनेक्ट होऊ शकतो. भिंतींमुळे काही फरक पडत नाही.
React च्या भाषेत, तुमच्या ॲपचे रूट प्रत्येक लेयरला कुरियर म्हणून काम करायला न सांगता, कंपोनंट ट्रीमधून डेटा ब्रॉडकास्ट करू शकते. कोणताही नेस्टेड कंपोनंट त्या ब्रॉडकास्टला सबस्क्राईब करू शकतो आणि त्याला नेमकी कशाची गरज आहे ते प्राप्त करू शकतो.
तीन मुख्य घटक
Context API तीन मुख्य भागांमध्ये विभागलेले आहे.
React.createContext() ब्रॉडकास्ट चॅनेल सेट करते. हे एक ऑब्जेक्ट रिटर्न करते ज्यामध्ये एक Provider आणि (जुन्या कोडमध्ये) एक Consumer असतो. एखाद्या विशिष्ट फीचरसाठी तुम्हाला हे फक्त एकदाच कॉल करावे लागते.
The Provider हा एक कंपोनंट आहे जो तुमच्या ट्रीचा एक भाग रॅप (wrap) करतो. तो value नावाचा एक प्रॉप स्वीकारतो. तुम्ही त्या प्रॉपमध्ये जे काही ठेवाल ते प्रत्येक वंशज (descendant) कंपोनंटला उपलब्ध होईल, मग ते कितीही खोलवर का असेना.
useContext हा असा Hook आहे जो फंक्शन कंपोनंटला त्या ब्रॉडकास्टचा वापर करण्याची परवानगी देतो. तुमच्या कंपोनंटच्या आत, तुम्ही तयार केलेला context ऑब्जेक्ट useContext मध्ये पास करता आणि तो सध्याची व्हॅल्यू रिटर्न करतो. बस एवढेच. कोणतेही रॅपर्स नाहीत, कोणतेही अतिरिक्त प्रॉप्स नाहीत.
Hooks येण्यापूर्वी, तुम्हाला रेंडर प्रॉप्ससह Consumer पॅटर्न वापरावा लागायचा. ते काम करायचे, पण त्यामुळे खूप जास्त इंडेंटेशन आणि रॅपरचा पसारा निर्माण व्हायचा. useContext ने या सर्वांना तुमच्या फंक्शन बॉडीमध्ये एका सिंगल लाईनमध्ये सुटसुटीत केले.
Context चा वापर कधी खरोखर फायदेशीर ठरतो
केवळ सवयीपोटी Context चा वापर करू नका. हे अशा डेटासाठी बनवले आहे जो तुमच्या ट्रीच्या वेगवेगळ्या शाखांमधील अनेक असंबद्ध कंपोनंट्समध्ये शेअर केला जातो. योग्य पर्याय खालीलप्रमाणे आहेत:
- Theme settings. केवळ लाइट किंवा डार्क मोडच नाही, तर स्पेसिंग टोकन्स, कलर पॅलेट्स आणि फॉन्ट स्केल्स. हे प्रत्येक स्टाईल केलेल्या बटन आणि मॉडेलमधून मॅन्युअली पास करणे लवकरच कंटाळवाणे होते.
- User authentication. लॉगिन स्टेटस, परमिशन अॅरे किंवा सध्याचा युजर ऑब्जेक्ट. तुमचे हेडर बार, डॅशबोर्ड विजेट आणि प्रायव्हेट राऊट गार्ड हे सर्व ट्रीच्या वेगवेगळ्या कोपऱ्यात असू शकतात.
- Language preferences. लोकेल स्ट्रिंग्स, डेट फॉरमॅट्स आणि करन्सी सिम्बॉल्स. फॉर्म लेबल्ससारख्या खोलवर असलेल्या कंपोनंट्सना यांची गरज असते, पण त्या मार्गावरील प्रत्येक पैरेंटला त्याबद्दल माहिती असण्याची गरज नाही.
- Shopping cart data. आयटम काउंट, एकूण किंमत आणि ॲड-टू-कार्ट फंक्शन्स. हेडर बॅज आणि चेकआउट पेजला एकच स्टेट लागते, पण ते सहसा पूर्णपणे वेगवेगळ्या लेआउट शाखांमध्ये असतात.
एक व्यावहारिक थीम स्विचर
Context प्रत्यक्ष कृतीत पाहण्याचा सर्वात स्पष्ट मार्ग म्हणजे थीम टॉगल (theme toggle). महत्त्वाच्या तपशिलांकडे दुर्लक्ष न करता तुम्ही ते कसे सेट करू शकता ते खाली दिले आहे.
प्रथम, एक ThemeContext.js फाईल तयार करा. React.createContext() कॉल करा आणि रिझल्ट स्टोअर करा. त्यानंतर useState किंवा useReducer वापरून सध्याची थीम मॅनेज करणारा एक ThemeProvider कंपोनंट तयार करा. तुमच्या कॉन्टेक्स्टच्या Provider मध्ये चिलड्रन्सना रॅप करा, आणि त्यात एक ऑब्जेक्ट पास करा ज्यामध्ये सध्याची थीम आणि ती टॉगल करण्यासाठी एक फंक्शन असेल. ThemeProvider आणि स्वतः context ऑब्जेक्ट दोन्ही एक्सपोर्ट करा.
दुसरे, तुमच्या ॲपच्या एन्ट्री पॉईंटवर जा. ThemeProvider इम्पोर्ट करा आणि तुमचे संपूर्ण ॲप्लिकेशन त्याच्याने रॅप करा. जर तुम्ही हे स्टेप वगळली, तर नंतर कॉन्टेक्स्ट वाचण्याचा प्रयत्न करणारी कोणतीही गोष्ट फक्त डिफॉल्ट व्हॅल्यूच पाहू शकेल.
तिसरे, Header किंवा Content कंपोनंटच्या आत, context ऑब्जेक्ट आणि useContext इम्पोर्ट करा. Hook कॉल करा, थीम आणि टॉगल फंक्शन डॅस्ट्रक्चर करा आणि तुमच्या CSS क्लासेसना कंडिशनल पद्धतीने लागू करा. टॉगल कॉल करणारे एक बटन जोडा. या कंपोनंटला त्याच्या पैरेंटकडून कधीही theme प्रॉप मिळत नाही. तो थेट हवेतून सिग्नल खेचतो.
प्रॉप ड्रिलिंग, कॉन्टेक्स्ट की रेडक्स?
या साधनांमधील निवड ही निष्ठा याबद्दल नसून तुमच्या स्टेटच्या (state) स्वरूपावर अवलंबून आहे.
Prop drilling दोन किंवा तीन स्तरांच्या खोलीसाठी (depth) अगदी योग्य आहे. हे स्पष्ट आहे, तुमच्या IDE मध्ये शोधणे सोपे आहे आणि डिपेंडेंसीज (dependencies) स्पष्ट ठेवते. जेव्हा तुम्ही सहा किंवा सात स्तरांमधून तोच प्रॉप (prop) पाठवू लागता, तेव्हाच समस्या उद्भवतात.
Context API React सोबतच येते. याचा अर्थ अतिरिक्त बंडल साईज (bundle size) आणि कोणतेही बाह्य सेटअप लागत नाही. हे लहान ते मध्यम आकाराच्या ग्लोबल स्टेटला (global state) उत्तम प्रकारे हाताळते, विशेषतः थीम किंवा युजर प्रोफाईल सारखा डेटा जो वारंवार बदलत नाही.
Redux साठी अतिरिक्त लायब्ररी इन्स्टॉल करणे आणि बॉयलरप्लेट (boilerplate) कोड लिहिणे आवश्यक असते. जेव्हा तुमचे स्टेट लॉजिक गुंतागुंतीचे असते, स्टेटचे अनेक भाग (slices) एकमेकांशी खोलवर जोडलेले असतात, किंवा जेव्हा तुम्हाला टाइम-ट्रॅव्हल डीबगिंग (time-travel debugging) आणि मिडलवेअरची (middleware) गरज असते, तेव्हा याचा फायदा होतो. साध्या ग्लोबल डेटासाठी, Redux वापरणे गरजेपेक्षा जास्त (overkill) आहे.
परफॉर्मन्सचे असे वास्तव ज्याबद्दल कोणीही बोलत नाही
येथेच तो फरक आहे जो ज्युनियर आणि सीनियर अंमलबजावणीमधील (implementations) फरक ठरवतो. जेव्हा Context Provider ची व्हॅल्यू बदलते, तेव्हा तो कॉन्टेक्स्ट वापरणारा प्रत्येक कंपोनंट पुन्हा रेंडर (re-render) होतो. त्या कंपोनंटला ज्या विशिष्ट भागाशी (slice) देणेघेणे आहे, तो भाग बदलला नसेल तरीही फरक पडत नाही. React नवीन रेफरन्स पाहतो आणि अपडेट शेड्यूल करतो.
जर तुम्ही तुमच्या संपूर्ण ॲप्लिकेशनची स्टेट एका मोठ्या StoreContext मध्ये टाकली, तर तुम्ही प्रभावीपणे तुमचे संपूर्ण UI एकमेकांना चिकटवून ठेवले आहे असे होईल. थीम सेटिंग बदलल्यामुळे तुमचे शॉपिंग कार्ट, डॅशबोर्ड चार्ट्स आणि नोटिफिकेशन लिस्ट पुन्हा रेंडर होतील. हे अनावश्यक काम आहे.
तुमचे कॉन्टेक्स्ट डोमेननुसार (domain) विभाजित करा. व्हिज्युअल सेटिंग्जसाठी ThemeContext, प्रोफाईल डेटासाठी UserContext आणि कॉमर्स स्टेटसाठी CartContext ठेवा. जर युजरने त्याचे डिस्प्ले नाव बदलले, तर प्रॉडक्ट ग्रिडला स्पर्श न करता तुमचे हेडर अपडेट होईल. तसेच, तुम्ही Provider च्या value प्रॉपमध्ये काय पास करत आहात याबद्दल सावध राहा. जर तुम्ही रेंडरिंग दरम्यान इनलाइन { theme, toggleTheme } सारखा ऑब्जेक्ट लिटरल पास केला, तर तुम्ही प्रत्येक रेंडरवर एक नवीन रेफरन्स तयार करता आणि अनावश्यक अपडेट्स ट्रिगर करता. जर व्हॅल्यूमध्ये फंक्शन्स किंवा नॉन-प्रिमिटिव्ह डेटा असेल, तर useMemo वापरून तो स्थिर करा.
तासनतास वाया घालवणारी काही चुका
दोन चुकांमुळे टीम्स वारंवार अडकतात.
कॉन्टेक्स्ट ऑब्जेक्ट एक्सपोर्ट करायला विसरणे. ThemeProvider कंपोनंट एक्सपोर्ट करणे आणि नंतर useContext(ThemeProvider) कॉल करण्याचा प्रयत्न करणे सोपे आहे. पण ते असे काम करत नाही. Hook ला createContext द्वारे परत मिळणारा कॉन्टेक्स्ट ऑब्जेक्ट हवा असतो, रॅपर कंपोनंट नाही. जर तुम्ही फक्त Provider एक्सपोर्ट केला, तर तुमच्या कन्स्युमर्सकडे (consumers) इम्पोर्ट करण्यासाठी काहीच उरणार नाही.
Provider च्या बाहेर useContext कॉल करणे. Hook तुम्हाला createContext मध्ये दिलेली डिफॉल्ट व्हॅल्यू परत करते. जर तुम्ही डिफॉल्ट व्हॅल्यू दिली नसेल, तर तुम्हाला undefined मिळेल. जर तुमचा कंपोनंट ट्री Provider पेक्षा वरच्या बाजूला कन्स्युमर रेंडर करत असेल, किंवा जर Provider पूर्णपणे गहाळ असेल, तर तुमचा डेटा मिळणार नाही. तुमचा index किंवा root फाईल खरोखर ॲपला रॅप (wrap) करते की नाही याची खात्री करा.
मुख्य निष्कर्ष
React Context ही स्टेट मॅनेजमेंटमधील क्रांती नाही. हे एका विशिष्ट समस्यांसाठी असलेले टार्गेटेड टूल आहे: प्रत्येक लेयरला पोस्ट ऑफिस बनवण्याशिवाय डेटा दूरच्या कंपोनंट्सपर्यंत पोहोचवणे. याचा वापर खरोखर ग्लोबल डेटासाठी करा, रेंडरिंग परफॉर्मन्स सुरक्षित ठेवण्यासाठी तुमचे कॉन्टेक्स्ट डोमेननुसार विभाजित ठेवा, आणि सिग्नल वाचण्याचा प्रयत्न करण्यापूर्वी नेहमी तुमच्या ट्रीला योग्य Provider ने रॅप करा. या सवयी आत्मसात करा, आणि तुमचे कंपोनंट ट्री स्वच्छ, वेगवान आणि समजण्यास सोपे राहतील.
