कौम्पोनॅन्ट्स के बीच state शेयरिंग

कभी-कभी आप चाहेंगे की दो कौम्पोनॅन्ट्स की state हमेशा एक साथ बदले। ऐसा करने के लिए पहले उन दोनों में से state को निकल दें, फिर उन्हें मूव कर के उनके निकटतम कॉमन पैरेंट के पास ले जाएँ, और फिर props के माध्यम से उनमें पास कर दें। इसे “लिफ्टिंग state अप” कहा जाता है, और React कोड लिखे जाते समय, किया जाने वाला यह सबसे कॉमन तरीका है।

आप सीखेंगे

  • लिफ्टिंग के द्वारा कौम्पोनॅन्ट्स के बीच state कैसे शेयर की जाये
  • कंट्रोल्ड और अन-कंट्रोल्ड कौम्पोनॅन्ट्स क्या होते हैं

लिफ्टिंग state अप, उदाहरण के माध्यम से

इस उदाहरण में एक पैरेंट Accordion कौम्पोनॅन्ट दो विभिन्न Panels को रेंडर करता है:

  • 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>
    </>
  );
}

गौर कीजिये किस तरह एक पैनल का बटन दबाना दूसरे पैनल को प्रभावित नहीं करता—दोनों स्वतंत्र हैं।

तीन कौम्पोनॅन्ट वाले ट्री का चित्र, जिसमे एक अक्कौरडियन लेबल वाला पैरेंट है और दो पैनल लेबल वाले चिल्ड्रेन। दोनों पैनल कौम्पोनॅन्ट में isActive false वैल्यू के साथ है।
तीन कौम्पोनॅन्ट वाले ट्री का चित्र, जिसमे एक अक्कौरडियन लेबल वाला पैरेंट है और दो पैनल लेबल वाले चिल्ड्रेन। दोनों पैनल कौम्पोनॅन्ट में isActive false वैल्यू के साथ है।

शुरुआत में दोनों Panels की isActive state false है, इसलिए दोनों कोलैप्स्ड दिख रहे हैं

पिछले चित्र की तरह, इसमें पहेले पैनल चाइल्ड कौम्पोनॅन्ट का isActive हाईलाइट किया गया है, जिसका मतलब है की इसकी isActive वैल्यू क्लिक द्वारा true पर सेट की गयी है। दूसरे पैनल कौम्पोनॅन्ट में अभी भी false वैल्यू है।
पिछले चित्र की तरह, इसमें पहेले पैनल चाइल्ड कौम्पोनॅन्ट का isActive हाईलाइट किया गया है, जिसका मतलब है की इसकी isActive वैल्यू क्लिक द्वारा true पर सेट की गयी है। दूसरे पैनल कौम्पोनॅन्ट में अभी भी false वैल्यू है।

किसी भी Panel की isActive state तभी अपडेट होगी जब उस Panel का बटन दबाया जायेगा

मान लीजिये यदि आप बदलाव के तौर पर चाहते हैं कि एक समय में सिर्फ एक ही पैनल एक्सपैंड हो। इस डिजाईन के मुताबिक़ एक पैनल के एक्सपैंड होने पर दूसरा कोलैप्स होना चाहिये। तो ये कैसे कर पाएंगे?

इन दोनों पेनल्स को संचालित करने के लिए, आपको तीन चरणों में इनके “state को लिफ्ट अप” करके इन्हें पैरेंट कौम्पोनॅन्ट में लाना होगा:

  1. चाइल्ड कौम्पोनॅन्ट में से state को हटाना
  2. कॉमन पैरेंट में से हार्ड कोड किया हुआ डाटा पास करना।
  3. कॉमन पैरेंट में state को फिर से ऐड करके उसे event-handler के साथ पास करना।

ऐसा करने से Accordion कौम्पोनॅन्ट दोनों Panels को संचालित कर पायेगा और एक समय में उनमें से एक ही एक्सपैंड होगा।

स्टेप 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 को बदलना संभव हो सका।

तीन कौम्पोनॅन्ट' वाले ट्री का चित्र, जिसमे एक अक्कौरडियन लेबल वाला पैरेंट है और दो पैनल लेबल वाले चिल्ड्रेन। अक्कौरडियन में शून्य वैल्यू की activeIndex है जिससे पहले पैनल में पास की गयी isActive वैल्यू true में बदल जाती है, और दुसरे पैनल में पास की गयी isActive वैल्यू false में बदल जाती है।
तीन कौम्पोनॅन्ट' वाले ट्री का चित्र, जिसमे एक अक्कौरडियन लेबल वाला पैरेंट है और दो पैनल लेबल वाले चिल्ड्रेन। अक्कौरडियन में शून्य वैल्यू की activeIndex है जिससे पहले पैनल में पास की गयी isActive वैल्यू true में बदल जाती है, और दुसरे पैनल में पास की गयी isActive वैल्यू false में बदल जाती है।

शुरुआत में Accordion का activeIndex 0 है , इसलिए पहले Panel को isActive = true मिलेगा

पिछले चित्र की तरह, इस चित्र में पैरेंट अक्कौरडियन कौम्पोनॅन्ट के पैनल का activeIndex हाईलाइट किया गया है, जिसका मतलब है की इसकी वैल्यू क्लिक द्वारा one पर सेट की गयी है। दोनों चिल्ड्रेन पैनल कौम्पोनॅन्ट्स का फ्लो भी हाईलाइट किया गया है, और हर चाइल्ड कौम्पोनॅन्ट को पास की गयी isActive वैल्यू विपरीत है: पहले पैनल के लिए false और दुसरे के लिए true।
पिछले चित्र की तरह, इस चित्र में पैरेंट अक्कौरडियन कौम्पोनॅन्ट के पैनल का activeIndex हाईलाइट किया गया है, जिसका मतलब है की इसकी वैल्यू क्लिक द्वारा one पर सेट की गयी है। दोनों चिल्ड्रेन पैनल कौम्पोनॅन्ट्स का फ्लो भी हाईलाइट किया गया है, और हर चाइल्ड कौम्पोनॅन्ट को पास की गयी isActive वैल्यू विपरीत है: पहले पैनल के लिए false और दुसरे के लिए true।

जब Accordion के activeIndex की state बदल कर 1 होगी, तब दूसरे Panel को isActive = true मिलेगा

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>
  );
}