FlowingDev

रिएक्ट का मैजिक शो: कंपोनेंट्स कैसे ज़िंदा होते हैं

जानें कि कैसे React, JSX को—जो HTML-जैसे सिंटैक्स और JavaScript का मिक्स है—कंपोनेंट्स, स्टेट और वर्चुअल DOM के ज़रिए इंटरैक्टिव वेब UI में बदलता है।

टूल आज़माएँ: React प्लेग्राउंड

एक वाक्य में

रिएक्ट एक JavaScript लाइब्रेरी है जो यूजर इंटरफेस (UI) बनाने के काम आती है। ये UI को 'कंपोनेंट्स' नाम के छोटे-छोटे, दोबारा इस्तेमाल हो सकने वाले कोड के टुकड़ों में तोड़ देती है, जो अपना डेटा बदलने पर अपने आप अपडेट हो जाते हैं।

यह कौन-सी समस्या हल करता है

ज़रा पुराने दिनों (समझ लीजिए 2010 के दशक की शुरुआत) में वेब डेवलपमेंट की कल्पना कीजिए। आपके पास एक वेबपेज होता था, और आप उसे इंटरैक्टिव बनाना चाहते थे। शायद आप चाहते थे कि एक "like" बटन पर क्लिक करने पर वह नीला हो जाए और एक काउंटर अपडेट हो जाए। jQuery जैसे टूल का उपयोग करके, आप कोड लिखते जो कहता: "पेज में बटन ढूंढो, एक क्लिक के लिए सुनो, और जब ऐसा हो, तो काउंटर एलिमेंट को ढूंढो और उसका टेक्स्ट बदलो, फिर बटन के आइकन को ढूंढो और उसका रंग बदलो।"

यह एक इम्परेटिव (imperative) अप्रोच है। आप ब्राउज़र को पेज बदलने के लिए स्टेप-बाय-स्टेप रेसिपी दे रहे हैं। यह एक लाइक बटन के लिए काम करता है। लेकिन एक पूरे सोशल मीडिया फ़ीड के बारे में क्या जिसमें सैकड़ों पोस्ट, कमेंट्स, नोटिफिकेशंस और एक लाइव चैट विंडो हो, जो सब एक साथ अपडेट हो रहे हों? ये "रेसिपी" स्पैगेटी कोड (spaghetti code) का एक उलझा हुआ जाल बन जाती हैं। एक जगह किया गया बदलाव दूसरी जगह किसी चीज़ को अप्रत्याशित रूप से तोड़ सकता है। UI को एप्लिकेशन के डेटा के साथ कंसिस्टेंट रखना एक लगातार, गलतियों से भरी जंग थी।

फेसबुक बड़े पैमाने पर इसी बुरे सपने को जी रहा था। 2011 में, उन्होंने इस समस्या को आंतरिक रूप से हल करने के लिए रिएक्ट बनाया। रिएक्ट ने एक डिक्लेरेटिव (declarative) मॉडल पेश किया। ब्राउज़र को यह बताने के बजाय कि चीजें कैसे बदलनी हैं, आप बस यह बताते हैं कि किसी भी दिए गए स्टेट के लिए UI कैसा दिखना चाहिए। आप घोषणा करते हैं: "अगर पोस्ट liked है, तो बटन नीला होना चाहिए और गिनती 101 होनी चाहिए। अगर यह liked नहीं है, तो बटन ग्रे है और गिनती 100 है।" बस। जब liked स्टेट false से true में बदलता है, तो रिएक्ट बटन और काउंटर को अपडेट करने का सबसे कुशल तरीका खोजने का काम खुद करता है। यह "कैसे" को संभालता है, ताकि आप "क्या" पर ध्यान केंद्रित कर सकें।

इम्परेटिव से डिक्लेरेटिव सोच की ओर यह बदलाव एक क्रांति थी। इसने जटिल, डेटा-संचालित UI बनाने को अधिक अनुमानित, स्केलेबल और डीबग करने में आसान बना दिया।

हुड के नीचे यह कैसे काम करता है

रिएक्ट का 'जादू' असल में कोई जादू नहीं है; यह तीन मुख्य विचारों का एक चतुर संयोजन है: JSX, कंपोनेंट्स और स्टेट, और वर्चुअल DOM।

JSX: JavaScript पर चढ़ा मीठा खोल

जब आप रिएक्ट कोड देखते हैं, तो आपको कुछ ऐसा दिखाई देता है जो HTML जैसा दिखता है और सीधे JavaScript में एम्बेड किया हुआ है। यह JSX (JavaScript XML) है।

const greeting = <h1>Hello, FlowingDev user!</h1>;

आपके ब्राउज़र को कोई अंदाज़ा नहीं है कि इसका क्या करना है। यह मान्य JavaScript नहीं है। इस कोड के चलने से पहले, इसे Babel जैसे टूल से "ट्रांसपाइल" (transformed) किया जाना ज़रूरी है। ऊपर दी गई JSX लाइन एक नियमित JavaScript फ़ंक्शन कॉल में बदल जाती है।

const greeting = React.createElement("h1", null, "Hello, FlowingDev user!");

हर JSX टैग एक React.createElement() कॉल बन जाता है। यह फ़ंक्शन कॉल वास्तविक HTML एलिमेंट नहीं बनाता है। यह एक हल्का JavaScript ऑब्जेक्ट बनाता है जो उस एलिमेंट का वर्णन करता है जिसे आप बनाना चाहते हैं—इसका प्रकार (h1), इसके गुण (इस मामले में कोई नहीं, इसलिए null), और इसके बच्चे ("Hello...")। ये ऑब्जेक्ट्स वर्चुअल DOM के बिल्डिंग ब्लॉक्स हैं।

तो, JSX सिर्फ "सिंटैक्टिक शुगर" (syntactic sugar) है। यह हमें एक परिचित, HTML-जैसे सिंटैक्स के साथ अपने UI का वर्णन करने देता है, लेकिन हुड के नीचे, हम सिर्फ JavaScript ऑब्जेक्ट्स का एक ट्री बना रहे हैं।

कंपोनेंट्स और स्टेट: इस ऑपरेशन का दिमाग

एक कंपोनेंट एक JavaScript फंक्शन है जो कुछ JSX रिटर्न करता है। यह UI का एक दोबारा इस्तेमाल किया जा सकने वाला, आत्मनिर्भर टुकड़ा है।

function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>;
}

यह Greeting कंपोनेंट सरल है। यह इनपुट के रूप में एक name लेता है (जिसे "prop" कहा जाता है) और एक हेडिंग रेंडर करता है। लेकिन क्या होगा अगर हम चाहते हैं कि एक कंपोनेंट चीजों को याद रखे और समय के साथ बदले, जैसे एक काउंटर? उसके लिए, हमें "स्टेट" (state) की आवश्यकता है।

स्टेट वह डेटा है जो एक कंपोनेंट का "अपना" होता है और बदल सकता है। रिएक्ट इसे मैनेज करने के लिए "हुक्स" (hooks) नामक विशेष फ़ंक्शन प्रदान करता है। सबसे आम है useState।

import { useState } from 'react';

function Counter() {
  // "count" is our state variable.
  // "setCount" is the *only* function we can use to update it.
  const [count, setCount] = useState(0); // The initial value is 0.

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

जब आप setCount(count + 1) को कॉल करते हैं, तो आप सिर्फ एक वेरिएबल नहीं बदल रहे हैं। आप रिएक्ट को बता रहे हैं: "अरे, इस Counter कंपोनेंट का स्टेट बदल गया है। कृपया नया UI पाने के लिए इस फ़ंक्शन को फिर से चलाएं, और फिर स्क्रीन को अपडेट करें।" रिएक्ट फिर कंपोनेंट को "re-render" करता है, अपडेटेड count के साथ नया JSX प्राप्त करता है, और कुशलतापूर्वक वेबपेज को अपडेट करता है।

वर्चुअल DOM: रिएक्ट का गुप्त हथियार

वास्तविक ब्राउज़र DOM (HTML एलिमेंट्स का लाइव ट्री) को सीधे बदलना धीमा है। यह वैसा ही है जैसे किसी कमरे को फिर से सजाने के लिए आप सभी दीवारों को तोड़कर फिर से बनाएँ, भले ही आप सिर्फ एक तस्वीर का फ्रेम हटाना चाहते हों।

रिएक्ट अपने गुप्त हथियार: वर्चुअल DOM (VDOM) के साथ इस महंगे काम से बचता है। VDOM बस DOM की एक कॉपी है जो JavaScript मेमोरी में रखी जाती है। यह वही React.createElement ऑब्जेक्ट्स का ट्री है जिसके बारे में हमने बात की थी।

यहाँ प्रक्रिया है, जिसे रिकंसीलिएशन (reconciliation) के रूप में जाना जाता है:

  1. प्रारंभिक रेंडर: जब Counter पहली बार रेंडर होता है, तो रिएक्ट एक VDOM ट्री बनाता है जो एक div का वर्णन करता है जिसमें "You clicked 0 times" टेक्स्ट वाला एक p और एक button होता है। फिर यह इसका उपयोग पेज पर वास्तविक DOM एलिमेंट्स बनाने के लिए करता है।
  2. स्टेट में बदलाव: आप बटन पर क्लिक करते हैं। setCount(1) कॉल होता है।
  3. री-रेंडर और नया VDOM: रिएक्ट Counter फ़ंक्शन को फिर से चलाता है। यह एक नया VDOM ट्री बनाता है। यह नया ट्री पुराने वाले के लगभग समान है, लेकिन p अब "You clicked 1 times" टेक्स्ट का वर्णन करता है।
  4. डिफिंग (Diffing): रिएक्ट का "डिफिंग" एल्गोरिथ्म नए VDOM ट्री की तुलना पुराने वाले से करता है। यह तेजी से उनके माध्यम से जाता है और अंतर ढूंढता है। इस मामले में, यह केवल एक अंतर पाता है: <p> टैग के अंदर का टेक्स्ट बदल गया है।
  5. बैच अपडेट: रिएक्ट इस एक छोटे से बदलाव को लेता है और इसे वास्तविक DOM पर लागू करता है। यह div या button को नहीं छूता; यह सिर्फ p के टेक्स्ट को अपडेट करता है।

यह डिफ-एंड-पैच प्रक्रिया अविश्वसनीय रूप से तेज़ है। अपडेट्स को बैच करके और धीमे, वास्तविक DOM के सीधे हेरफेर को कम करके, रिएक्ट UI को तेज़ और रिस्पॉन्सिव महसूस कराता है, भले ही जटिल डेटा तेजी से बदल रहा हो।

असल दुनिया की कहानियाँ

गायब हो रहे फ़ॉर्म डेटा का मामला

एक जूनियर डेवलपर को एक जटिल मल्टी-स्टेप यूजर रजिस्ट्रेशन फॉर्म बनाने का काम सौंपा गया था। प्रत्येक स्टेप एक अलग कंपोनेंट था। एक समय पर, उन्हें स्टेप 2 में एक अतिरिक्त इनपुट फ़ील्ड को शर्त के आधार पर दिखाने के लिए एक छोटे चेकबॉक्स की आवश्यकता थी। उन्होंने इसे लागू किया, लेकिन अचानक, उनके पास एक अजीब बग आ गया: जब भी कोई उपयोगकर्ता उस बॉक्स को चेक करता, तो स्टेप 2 में दर्ज किया गया सारा डेटा गायब हो जाता।

इसका असली कारण 'keys' की गलतफहमी थी। अतिरिक्त फ़ील्ड दिखाने के लिए, डेवलपर अपने JSX की संरचना को इस तरह से बदल रहा था कि रिएक्ट को लगा कि स्टेप 2 का पूरा फ़ॉर्म कंपोनेंट एक नया और अलग कंपोनेंट है। इसे पहचानने के लिए एक स्थिर key प्रॉप के बिना, रिएक्ट ने पुराने कंपोनेंट (उसके सभी स्टेट, यानी उपयोगकर्ता के इनपुट के साथ) को नष्ट कर दिया और उसकी जगह एक बिल्कुल नया कंपोनेंट माउंट कर दिया।

सबक: key प्रॉप सिर्फ लिस्ट के लिए नहीं है। यह एक कंपोनेंट के लिए एक स्थिर पहचान है। यह समझना कि रिएक्ट रेंडर के दौरान कंपोनेंट्स को ट्रैक करने के लिए कीज़ का उपयोग कैसे करता है, स्टेट को बनाए रखने और अप्रत्याशित रीसेट से बचने के लिए महत्वपूर्ण है।

अनंत API हैमर का किस्सा

एक टीम एक डैशबोर्ड बना रही थी जो लाइव स्टॉक डेटा प्रदर्शित करता था। एक डेवलपर ने useEffect हुक का उपयोग करके एक विशिष्ट स्टॉक की नवीनतम कीमत लाने के लिए एक कंपोनेंट लिखा। कोड कुछ इस तरह दिखता था:

useEffect(() => {
  fetchStockPrice(stockSymbol).then(price => {
    setPrice(price); // Update the state with the new price
  });
}); // Whoops, no dependency array!

जैसे ही कंपोनेंट लोड हुआ, ऐप फ्रीज हो गया, डेवलपर के लैपटॉप का पंखा तेजी से घूमने लगा, और नेटवर्क मॉनिटरिंग में प्रति सेकंड हजारों एक जैसे API अनुरोध भेजे जा रहे थे। उन्होंने एक अनंत लूप बना दिया था। useEffect ने डेटा प्राप्त किया और setPrice को कॉल किया। स्टेट अपडेट के कारण कंपोनेंट फिर से रेंडर हुआ। चूँकि useEffect में कोई डिपेंडेंसी ऐरे नहीं था, यह हर रेंडर के बाद फिर से चला, फिर से डेटा लाया, फिर से स्टेट अपडेट किया, जिससे एक और री-रेंडर हुआ, और यह हमेशा के लिए चलता रहा।

सबक: useEffect जैसे रिएक्ट हुक्स शक्तिशाली लेकिन सटीक उपकरण हैं। आपको यह बताने के लिए एक "डिपेंडेंसी ऐरे" (dependency array) प्रदान करना होगा कि रिएक्ट को इफ़ेक्ट को कब फिर से चलाना है। इसे छोड़ देने का मतलब है "हर एक रेंडर के बाद चलाओ," जो स्टेट अपडेट के साथ तबाही का नुस्खा है।

आम गलतियाँ और जाल

  • स्टेट को सीधे बदलना (mutate करना)। कभी भी myState.prop = 'value' या myArray.push('item') न करें। रिएक्ट का चेंज डिटेक्शन ऑब्जेक्ट और ऐरे की पहचान पर निर्भर करता है। जब आप setState को कॉल करते हैं, तो आपको रिएक्ट को बदलाव का पता लगाने के लिए एक नया ऑब्जेक्ट या ऐरे (उदाहरण के लिए, स्प्रेड सिंटैक्स ... का उपयोग करके) प्रदान करना होगा।
  • लिस्ट में key भूल जाना। जब आप .map() का उपयोग करके आइटम्स की एक लिस्ट रेंडर करते हैं, तो आपको प्रत्येक एलिमेंट को एक यूनिक और स्थिर key प्रॉप (जैसे item.id) देना होगा। इसी तरह रिएक्ट का डिफिंग एल्गोरिथ्म कुशलता से ट्रैक कर सकता है कि कौन से आइटम स्थानांतरित हुए हैं, जोड़े गए हैं, या हटा दिए गए हैं। ऐरे इंडेक्स को की के रूप में उपयोग करना एक आम एंटी-पैटर्न है जो बग्स को जन्म दे सकता है।
  • JSX को HTML समझना। यह करीब है, लेकिन एक जैसा नहीं है। आपको class के बजाय className, for के बजाय htmlFor, और स्टाइल प्रॉपर्टीज़ के लिए कैमलकेस (backgroundColor न कि background-color) का उपयोग करना होगा। साथ ही, सभी टैग्स को बंद किया जाना चाहिए, इसलिए एक इमेज <img ... /> है, न कि <img>।
  • हुक्स को जादू समझना। useState और useEffect जैसे हुक्स सख्त नियमों का पालन करते हैं (उदाहरण के लिए, उन्हें केवल एक फंक्शन कंपोनेंट के टॉप लेवल पर ही कॉल किया जा सकता है)। वे रेंडर के बीच एक सुसंगत कॉल ऑर्डर पर भरोसा करके काम करते हैं। उन्हें लूप या कंडीशंस के अंदर कॉल करने की कोशिश करने से आपका ऐप टूट जाएगा।

यह आपके रडार पर क्यों होना चाहिए

रिएक्ट सिर्फ एक लाइब्रेरी नहीं है; यह एक इकोसिस्टम और सोचने का एक तरीका है। यह आधुनिक वेब के एक बड़े हिस्से को शक्ति प्रदान करता है, इंस्टाग्राम और नेटफ्लिक्स जैसी विशाल साइटों से लेकर अनगिनत स्टार्टअप्स और आंतरिक उपकरणों तक। रिएक्ट सीखना एक कंपोनेंट-आधारित आर्किटेक्चर सीखना है जो दोबारा इस्तेमाल करने योग्य, परीक्षण योग्य और रखरखाव योग्य कोड को बढ़ावा देता है।

भले ही आप रोज़ रिएक्ट का उपयोग न करें, इसके मूल सिद्धांतों—डिक्लेरेटिव UI, स्टेट मैनेजमेंट, और VDOM— को समझने से आप एक बेहतर डेवलपर बनेंगे। इन अवधारणाओं ने लगभग हर दूसरे आधुनिक UI फ्रेमवर्क, Vue से लेकर Svelte तक, को प्रभावित किया है। इसके अलावा, रिएक्ट नेटिव के साथ, उन्हीं स्किल्स और सिद्धांतों का उपयोग iOS और Android के लिए नेटिव मोबाइल ऐप बनाने के लिए किया जा सकता है। यह एक शक्तिशाली पैराडाइम है जो आज यूजर इंटरफेस पर काम करने वाले किसी भी डेवलपर के लिए आवश्यक है।

और गहराई में जाएं

  • React Docs (Official) - आधिकारिक डॉक्यूमेंटेशन शुरू करने के लिए सबसे अच्छी जगह है, जिसमें एक व्यापक ट्यूटोरियल और गहरी जानकारी है।
  • Introducing JSX (React Docs) - JSX की आधिकारिक व्याख्या और यह HTML से कैसे अलग है।
  • Reconciliation (React Docs) - वह एल्गोरिदम जिसका उपयोग रिएक्ट दो VDOM ट्री की तुलना करने और UI को कुशलतापूर्वक अपडेट करने के लिए करता है।
  • The Rules of Hooks (React Docs) - useState और useEffect जैसे हुक्स का उपयोग करते समय आपको जिन आवश्यक नियमों का पालन करना चाहिए।
  • MDN: Introduction to the DOM - यह समझने के लिए कि रिएक्ट किस चीज में हेरफेर कर रहा है, डॉक्यूमेंट ऑब्जेक्ट मॉडल को समझना महत्वपूर्ण है।

एडिटर कोड को कैसे समझता है

रंग भाषा के पार्सर से आते हैं, प्रोग्राम चलाने से नहीं। CodeMirror और Lezer ब्राउज़र में ही टोकन और व्याकरण पहचानते हैं। सुझावों में कीवर्ड, समर्थित स्थानीय नाम, स्निपेट और छोटा अंतर्निहित संदर्भ शामिल हैं; दर्ज किए गए प्रतीक पर माउस रखने से उसका सिग्नेचर दिखता है। सुझाव आने का मतलब यह नहीं कि रनटाइम में वह API मौजूद है। समर्थित पार्सर गलत सिंटैक्स पहचानते हैं, लेकिन यह टाइप जाँच या वास्तविक निष्पादन का विकल्प नहीं है। सही सिंटैक्स वाला कोड भी विफल हो सकता है। Ruby और Lua टोकन-आधारित मोड इस्तेमाल करते हैं, इसलिए निदान सीमित हैं। कोई दूरस्थ भाषा सर्वर नहीं है; कोड डिवाइस से बाहर नहीं जाता।

CodeMirror & Lezer: MIT.

थ्योरी हो गई। अब हाथ आज़माइए — 100% आपके ब्राउज़र में।

टूल आज़माएँ: React प्लेग्राउंड