कौम्पोनॅन्ट्स के बीच state शेयरिंग
कभी-कभी आप चाहेंगे की दो कौम्पोनॅन्ट्स की state हमेशा एक साथ बदले। ऐसा करने के लिए पहले उन दोनों में से state को निकल दें, फिर उन्हें मूव कर के उनके निकटतम कॉमन पैरेंट के पास ले जाएँ, और फिर props के माध्यम से उनमें पास कर दें। इसे “लिफ्टिंग state अप” कहा जाता है, और React कोड लिखे जाते समय, किया जाने वाला यह सबसे कॉमन तरीका है।
आप सीखेंगे
- लिफ्टिंग के द्वारा कौम्पोनॅन्ट्स के बीच state कैसे शेयर की जाये
- कंट्रोल्ड और अन-कंट्रोल्ड कौम्पोनॅन्ट्स क्या होते हैं
लिफ्टिंग state अप, उदाहरण के माध्यम से
इस उदाहरण में एक पैरेंट Accordion
कौम्पोनॅन्ट दो विभिन्न Panel
s को रेंडर करता है:
Accordion
Panel
Panel
प्रत्येक Panel
कौम्पोनॅन्ट में एक boolean isActive
state है जो ये निर्धारित करती है कि कौन स कंटेंट दिखाई देगा।
दोनों पैनल्स के Show बटन को दबाएँ:
import { useState } from 'react'; function Panel({ title, children }) { const [isActive, setIsActive] = useState(false); return ( <section className="panel"> <h3>{title}</h3> {isActive ? ( <p>{children}</p> ) : ( <button onClick={() => setIsActive(true)}> Show </button> )} </section> ); } export default function Accordion() { return ( <> <h2>Almaty, Kazakhstan</h2> <Panel title="About"> 20 लाख की आबादी के साथ, अलमाती कज़ख़िस्तान का सबसे बड़ा शहर है। 1929 से 1997 तक, वह उसकी राजधानी थी। </Panel> <Panel title="Etymology"> यह नाम <span lang="kk-KZ">алма</span> से आता है, जो "सेब" का कज़ाख शब्द है और अक्सर "सेब से भरपूर" में अनुवाद करता है। असल में, अलमाती के आस पास के क्षेत्र को सेब का पैतृक घर माना जाता है, और जंगली <i lang="la">Malus sieversii</i> को सेब के पूर्वज होने का संभावित उम्मीदवार माना जाता है। </Panel> </> ); }
गौर कीजिये किस तरह एक पैनल का बटन दबाना दूसरे पैनल को प्रभावित नहीं करता—दोनों स्वतंत्र हैं।
मान लीजिये यदि आप बदलाव के तौर पर चाहते हैं कि एक समय में सिर्फ एक ही पैनल एक्सपैंड हो। इस डिजाईन के मुताबिक़ एक पैनल के एक्सपैंड होने पर दूसरा कोलैप्स होना चाहिये। तो ये कैसे कर पाएंगे?
इन दोनों पेनल्स को संचालित करने के लिए, आपको तीन चरणों में इनके “state को लिफ्ट अप” करके इन्हें पैरेंट कौम्पोनॅन्ट में लाना होगा:
- चाइल्ड कौम्पोनॅन्ट में से state को हटाना।
- कॉमन पैरेंट में से हार्ड कोड किया हुआ डाटा पास करना।
- कॉमन पैरेंट में state को फिर से ऐड करके उसे event-handler के साथ पास करना।
ऐसा करने से Accordion
कौम्पोनॅन्ट दोनों Panel
s को संचालित कर पायेगा और एक समय में उनमें से एक ही एक्सपैंड होगा।
स्टेप 1: चाइल्ड कौम्पोनॅन्ट में से state निकालना
आप Panel
के isActive
का कण्ट्रोल उसके पैरेंट कौम्पोनॅन्ट को देंगे। इसका मतलब है कि पैरेंट कौम्पोनॅन्ट isActive
को Panel
तक एक prop की तरह पास करेगा। आप Panel
कौम्पोनॅन्ट से यह लाइन हटाने से शुरुआत कर सकते हैं:
const [isActive, setIsActive] = useState(false);
और इसके बजाये, Panel
के props की लिस्ट में isActive
को जोड़ दें:
function Panel({ title, children, isActive }) {
अब Panel
का पैरेंट कौम्पोनॅन्ट isActive
को prop की तरह पास कर के नियंत्रित कर पायेगा। ठीक इसके विपरीत, अब Panel
कौम्पोनॅन्ट के isActive
की वैल्यू पर कोई नियंत्रित नहीं रह जायेगा—ये अब पैरेंट कौम्पोनॅन्ट पर निर्भर है!
स्टेप 2: कॉमन पैरेंट कौम्पोनॅन्ट से हार्ड कोडेड डाटा को पास करना
अब state को लिफ्ट अप करने के लिए आपको उस निकटतम कॉमन पैरेंट कौम्पोनॅन्ट का पता लगाना है, जो उन दोनों चाइल्ड कौम्पोनॅन्ट का पैरेंट है जिन्हें आप संचालित करना चाहते हैं:
Accordion
(निकटतम कॉमन पैरेंट)Panel
Panel
इस उदाहरण में, यह Accordion
कौम्पोनॅन्ट है। चूँकि यह दोनों पेनल्स के उपर है और उनके props को कंट्रोल कर सकता है, इसलिए यह अब ये वर्तमान में एक्टिव पैनल के लिए एक “सोर्स ऑफ़ ट्रुथ” बन जायेगा। अब Accordion
कौम्पोनॅन्ट से, दोनों पेनल्स की तरफ isActive
की एक हार्ड कोडेड वैल्यू पास करवाएं (उदाहरण के लिए, true
):
import { useState } from 'react'; export default function Accordion() { return ( <> <h2>Almaty, Kazakhstan</h2> <Panel title="About" isActive={true}> 20 लाख की आबादी के साथ, अलमाती कज़ख़िस्तान का सबसे बड़ा शहर है। 1929 से 1997 तक, वह उसकी राजधानी थी। </Panel> <Panel title="Etymology" isActive={true}> यह नाम <span lang="kk-KZ">алма</span> से आता है, जो "सेब" का कज़ाख शब्द है और अक्सर "सेब से भरपूर" में अनुवाद करता है। असल में, अलमाती के आस पास के क्षेत्र को सेब का पैतृक घर माना जाता है, और जंगली <i lang="la">Malus sieversii</i> को सेब के पूर्वज होने का संभावित उम्मीदवार माना जाता है। </Panel> </> ); } function Panel({ title, children, isActive }) { return ( <section className="panel"> <h3>{title}</h3> {isActive ? ( <p>{children}</p> ) : ( <button onClick={() => setIsActive(true)}> Show </button> )} </section> ); }
अब Accordion
कौम्पोनॅन्ट के हार्ड कोडेड isActive
वैल्यूज को एडिट करें और स्क्रीन पर उसका रिजल्ट देखें।
स्टेप 3 : कॉमन पैरेंट में state को ऐड करना
State को लिफ्ट अप करते समय, कई बार उस state में हम क्या स्टोर कर रहे हैं, उसका नेचर बदल सकता है।
ऐसे में एक समय पर सिर्फ एक ही पैनल एक्टिव रहना चाहिए। इसका मतलब है की Accordion
कॉमन पैरेंट कौम्पोनॅन्ट को यह ट्रैक करते रहना होगा की कौन सा पैनल एक्टिव है। boolean
वैल्यू के बजाये , वो state वेरिएबल के लिए, एक नंबर को एक्टिव Panel
के इंडेक्स की तरह इस्तेमाल कर सकता है:
const [activeIndex, setActiveIndex] = useState(0);
जब activeIndex
0
हो तो पहला पैनल एक्टिव है, और अगर ये 1
हो तो दूसरा।
किसी भी Panel
में “Show” का बटन दबाने पर Accordion
में active index बदल जाना चाहिए। एक Panel
सीधे ही activeIndex
state सेट नहीं कर सकता क्योंकि वह Accordion
के अन्दर डिफाइन किया गया है। Accordion
कौम्पोनॅन्ट को साफ तौर पर Panel
कौम्पोनॅन्ट को अपनी state बदलने की इजाज़त देनी होगी, जिसके लिए उसे event-handler को prop की तरह पास कराना होगा:
<>
<Panel
isActive={activeIndex === 0}
onShow={() => setActiveIndex(0)}
>
...
</Panel>
<Panel
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
...
</Panel>
</>
Panel
के अंदर का <button>
अब onShow
prop को क्लिक event handler की तरह इस्तेमाल करेगा:
import { useState } from 'react'; export default function Accordion() { const [activeIndex, setActiveIndex] = useState(0); return ( <> <h2>Almaty, Kazakhstan</h2> <Panel title="About" isActive={activeIndex === 0} onShow={() => setActiveIndex(0)} > 20 लाख की आबादी के साथ, अलमाती कज़ख़िस्तान का सबसे बड़ा शहर है। 1929 से 1997 तक, वह उसकी राजधानी थी। </Panel> <Panel title="Etymology" isActive={activeIndex === 1} onShow={() => setActiveIndex(1)} > यह नाम <span lang="kk-KZ">алма</span> से आता है, जो "सेब" का कज़ाख शब्द है और अक्सर "सेब से भरपूर" में अनुवाद करता है। असल में, अलमाती के आस पास के क्षेत्र को सेब का पैतृक घर माना जाता है, और जंगली <i lang="la">Malus sieversii</i> को सेब के पूर्वज होने का संभावित उम्मीदवार माना जाता है। </Panel> </> ); } function Panel({ title, children, isActive, onShow }) { return ( <section className="panel"> <h3>{title}</h3> {isActive ? ( <p>{children}</p> ) : ( <button onClick={onShow}> Show </button> )} </section> ); }
इस तरह लिफ्टिंग states अप पूरा हुआ! State को कॉमन पैरेंट में मूव करने से दोनों पैनल्स के बीच संचालन संभव हो सका। दो “is shown” फ्लैग्स के बजाये, active index इस्तेमाल करने से, एक समय में एक ही पैनल एक्टिव रख पाना संभव हो सका। और event-handler को चाइल्ड तक पास-डाउन करने से चाइल्ड द्वारा पैरेंट state को बदलना संभव हो सका।
Deep Dive
लोकल state वाले कौम्पोनॅन्ट को “अनियंत्रित” कहा जाना आम बात है। उदाहरण के लिए, isActive
state वेरिएबल के साथ वाले ओरिजिनल Panel
कौम्पोनॅन्ट को इसीलिए अनियंत्रित कहा जायेगा क्युकी इसका पैरेंट उसके पैनल के एक्टिव होने या न होने को प्रभावित नहीं कर सकता।
इसके ठीक विपरीत, एक कौम्पोनॅन्ट को “नियंत्रित” कहा जायेगा यदि उसकी महत्वपूर्ण सूचना, उसके अपने लोकल state के बजाये props द्वारा चलायी जाएगी। इस कारण, पैरेंट कौम्पोनॅन्ट उसके स्वभाव को पूरी तरह से स्पष्ट करता है। isActive
prop वाला अंतिम कौम्पोनॅन्ट, Accordion
कौम्पोनॅन्ट से नियंत्रीत होगा।
अनियंत्रित कौम्पोनॅन्ट, अपनी कम कॉन्फ़िगरेशन ज़रूरतों के कारण अपने पेरेंट्स के अन्दर आसानी से इस्तेमाल किये जा सकते है। परन्तु उनको समन्वित करने के लिए ये कम लचीले है। नियंत्रित कौम्पोनॅन्ट अत्यधिक लचीले है परन्तु उसके लिए उनके पैरेंट कौम्पोनॅन्ट को props द्वारा पूरी तरह कॉन्फ़िगर करने की आवश्यकता है।
प्रमाणिक तौर पर नियंत्रित और अनियंत्रित पूरी तरह से टेक्निकल शब्द नहीं है—प्रत्येक कौम्पोनॅन्ट अधिकतर लोकल state और props का मिश्रण है। परन्तु ये कौम्पोनॅन्ट्स के प्रारूप और क्षमताओं के बारे में बताने का कारगर तरीका है।
कौम्पोनॅन्ट के बारे में लिखते समय इस बात का ख्याल रखे की कौनसी सूचना नियंत्रित (props द्वारा) और कौनसी अनियंत्रित (states द्वारा) है। पर यह आप बाद में बदलकर रीफैक्टर कर सकते है।
हर state के लिए एक ही सोर्स ऑफ़ ट्रुथ
React एप्लीकेशन में, कई कौम्पोनॅन्ट का अपना एक state होगा। इनपुट्स की तरह के कुछ state अपने लीफ कौम्पोनॅन्ट (ट्री के सबसे निचे में रहने वाले कौम्पोनॅन्ट्स) के नजदीक “रह” सकते हैं। कुछ state एप्प के टॉप पर “रह” सकते हैं। उदाहरण के लिए कुछ client-side राउटिंग लाइब्रेरीज इम्प्लीमेंट करते समय उनके करंट state को React state में स्टोर किया जाता है और फिर props की मदद से पास किया जाता है!
किसी भी state के प्रत्येक विशिष्ट टुकड़े के लिए, आप वह कौम्पोनॅन्ट चुनेंगे जिसका वे “हिस्सा” हैं। इस सिद्धांत को “सच्चाई का एक ही स्त्रोत” या “सिंगल सोर्स ऑफ़ ट्रुथ” कहा जाता है। इसका मतलब यह नहीं की सारे state एक ही जगह रहते हैं—बल्कि state के हर हिस्से के लिए, एक विशिष्ट कौम्पोनॅन्ट है जो इस सूचना को अपने पास रखता है। कौम्पोनॅन्ट के बीच शेयर्ड state की डुप्लिकेटिंग करने के बजाये, आप उनको लिफ्ट करके, उनके कॉमन शेयर्ड पैरेंट तक ले जायेंगे और उन्हें उनके जरूरतमंद चिल्ड्रेन तक पास कर देंगे।
आपका एप्प आपके काम करते करते बदलता जायेगा। State के हर हिस्से को उसकी “रहने” की सही जगह पहुंचाने तक, बार-बार state को अप या डाउन ले जाना सामान्य है। यह सब इस प्रोसेस का हिस्सा है!
यदि आप कुछ और कौम्पोनॅन्ट के साथ प्रैक्टिस करने का अनुभव लेना चाहते हैं, तो इसके लिए React में सोचना लेख पढ़ें।
Recap
- यदि आप दो कौम्पोनॅन्ट्स के बीच तालमेल करना चाहते हैं, तब उनकी state को उनके कॉमन पैरेंट पर मूव करें।
- फिर इनफार्मेशन को कॉमन पैरेंट में से props की मदद से पास करें।
- अंत में इवेंट हैंडलर्स को पास-डाउन करें जिस से चिल्ड्रेन अपने पैरेंट state बदल सकें।
- बेहतर होगा यदि कौम्पोनॅन्ट्स को “कंट्रोल्ड” (props द्वारा ड्रिवन) या “अन-कंट्रोल्ड” (state द्वारा ड्रिवन) मानें।
Challenge 1 of 2: सिंक किये हुए इनपुट
ये दो इनपुट इंडिपेंडेंट हैं। इन्हें सिंक में रखने की कोशिश करें: एक इनपुट को एडिट करने पर दूसरा इनपुट उसकी टेक्स्ट से अपडेट होना चाहिए, और फिर वाईस वर्सा।
import { useState } from 'react'; export default function SyncedInputs() { return ( <> <Input label="First input" /> <Input label="Second input" /> </> ); } function Input({ label }) { const [text, setText] = useState(''); function handleChange(e) { setText(e.target.value); } return ( <label> {label} {' '} <input value={text} onChange={handleChange} /> </label> ); }