हिन्दी

रिएक्ट के कॉम्पोनेंट आर्किटेक्चर का गहन विश्लेषण, कंपोजिशन और इनहेरिटेंस की तुलना। जानें कि रिएक्ट कंपोजिशन को क्यों पसंद करता है और स्केलेबल कॉम्पोनेंट बनाने के लिए HOCs, रेंडर प्रॉप्स और हुक्स जैसे पैटर्न का अन्वेषण करें।

रिएक्ट कॉम्पोनेंट आर्किटेक्चर: इनहेरिटेंस पर कंपोजिशन की जीत क्यों होती है

सॉफ्टवेयर डेवलपमेंट की दुनिया में, आर्किटेक्चर सर्वोपरि है। हम अपने कोड को जिस तरह से संरचित करते हैं, वह उसकी स्केलेबिलिटी, मेंटेनेबिलिटी और पुन: प्रयोज्यता को निर्धारित करता है। रिएक्ट के साथ काम करने वाले डेवलपर्स के लिए, सबसे मौलिक वास्तुशिल्प निर्णयों में से एक यह है कि कॉम्पोनेंट्स के बीच लॉजिक और UI को कैसे साझा किया जाए। यह हमें ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग में एक क्लासिक बहस की ओर ले जाता है, जिसे रिएक्ट की कॉम्पोनेंट-आधारित दुनिया के लिए फिर से कल्पना की गई है: कंपोजिशन बनाम इनहेरिटेंस

यदि आप जावा या C++ जैसी क्लासिकल ऑब्जेक्ट-ओरिएंटेड भाषाओं की पृष्ठभूमि से आते हैं, तो इनहेरिटेंस एक स्वाभाविक पहली पसंद की तरह महसूस हो सकता है। यह 'is-a' संबंध बनाने के लिए एक शक्तिशाली अवधारणा है। हालांकि, आधिकारिक रिएक्ट डॉक्यूमेंटेशन एक स्पष्ट और मजबूत सिफारिश प्रदान करता है: "फेसबुक में, हम हजारों कॉम्पोनेंट्स में रिएक्ट का उपयोग करते हैं, और हमें ऐसा कोई भी उपयोग का मामला नहीं मिला है जहाँ हम कॉम्पोनेंट इनहेरिटेंस पदानुक्रम बनाने की सलाह देंगे।"

यह पोस्ट इस वास्तुशिल्प विकल्प का एक व्यापक अन्वेषण प्रदान करेगी। हम यह समझेंगे कि रिएक्ट के संदर्भ में इनहेरिटेंस और कंपोजिशन का क्या अर्थ है, यह प्रदर्शित करेंगे कि कंपोजिशन एक मुहावरेदार और बेहतर दृष्टिकोण क्यों है, और उन शक्तिशाली पैटर्न का पता लगाएंगे—हायर-ऑर्डर कॉम्पोनेंट्स से लेकर आधुनिक हुक्स तक—जो वैश्विक दर्शकों के लिए मजबूत और लचीले एप्लिकेशन बनाने के लिए कंपोजिशन को एक डेवलपर का सबसे अच्छा दोस्त बनाते हैं।

पुराने तरीके को समझना: इनहेरिटेंस क्या है?

इनहेरिटेंस ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग (OOP) का एक मुख्य स्तंभ है। यह एक नई क्लास (सबक्लास या चाइल्ड) को एक मौजूदा क्लास (सुपरक्लास या पैरेंट) के गुणों और तरीकों को प्राप्त करने की अनुमति देता है। यह एक कसकर-युग्मित (tightly-coupled) 'is-a' संबंध बनाता है। उदाहरण के लिए, एक GoldenRetriever एक Dog है, जो एक Animal है

गैर-रिएक्ट संदर्भ में इनहेरिटेंस

आइए इस अवधारणा को मजबूत करने के लिए एक सरल जावास्क्रिप्ट क्लास उदाहरण देखें:

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // Calls the parent constructor
    this.breed = breed;
  }

  speak() { // Overrides the parent method
    console.log(`${this.name} barks.`);
  }

  fetch() {
    console.log(`${this.name} is fetching the ball!`);
  }
}

const myDog = new Dog('Buddy', 'Golden Retriever');
myDog.speak(); // Output: "Buddy barks."
myDog.fetch(); // Output: "Buddy is fetching the ball!"

इस मॉडल में, Dog क्लास स्वचालित रूप से Animal से name प्रॉपर्टी और speak मेथड प्राप्त करती है। यह अपने स्वयं के तरीकों (fetch) को भी जोड़ सकती है और मौजूदा तरीकों को ओवरराइड कर सकती है। यह एक कठोर पदानुक्रम बनाता है।

रिएक्ट में इनहेरिटेंस क्यों विफल होता है

हालांकि यह 'is-a' मॉडल कुछ डेटा संरचनाओं के लिए काम करता है, लेकिन जब इसे रिएक्ट में UI कॉम्पोनेंट्स पर लागू किया जाता है तो यह महत्वपूर्ण समस्याएं पैदा करता है:

इन मुद्दों के कारण, रिएक्ट टीम ने लाइब्रेरी को एक अधिक लचीले और शक्तिशाली प्रतिमान के आसपास डिजाइन किया: कंपोजिशन।

रिएक्ट के तरीके को अपनाना: कंपोजिशन की शक्ति

कंपोजिशन एक डिजाइन सिद्धांत है जो 'has-a' (पास है) या 'uses-a' (उपयोग करता है) संबंध का पक्षधर है। इसके बजाय कि एक कॉम्पोनेंट दूसरा कॉम्पोनेंट हो, यह दूसरे कॉम्पोनेंट को रखता है या उनकी कार्यक्षमता का उपयोग करता है। कॉम्पोनेंट्स को बिल्डिंग ब्लॉक्स—जैसे लेगो ब्रिक्स—के रूप में माना जाता है, जिन्हें एक कठोर पदानुक्रम में बंद किए बिना जटिल UI बनाने के लिए विभिन्न तरीकों से जोड़ा जा सकता है।

रिएक्ट का कंपोजिशनल मॉडल अविश्वसनीय रूप से बहुमुखी है, और यह कई प्रमुख पैटर्न में प्रकट होता है। आइए उन्हें सबसे बुनियादी से लेकर सबसे आधुनिक और शक्तिशाली तक का अन्वेषण करें।

तकनीक 1: props.children के साथ कंटेनमेंट

कंपोजिशन का सबसे सीधा रूप कंटेनमेंट है। यह वह जगह है जहाँ एक कॉम्पोनेंट एक सामान्य कंटेनर या 'बॉक्स' के रूप में कार्य करता है, और इसकी सामग्री एक पैरेंट कॉम्पोनेंट से पास की जाती है। रिएक्ट के पास इसके लिए एक विशेष, अंतर्निहित प्रॉप है: props.children

कल्पना कीजिए कि आपको एक `Card` कॉम्पोनेंट की आवश्यकता है जो किसी भी सामग्री को एक सुसंगत बॉर्डर और शैडो के साथ लपेट सके। इनहेरिटेंस के माध्यम से `TextCard`, `ImageCard`, और `ProfileCard` वेरिएंट बनाने के बजाय, आप एक सामान्य `Card` कॉम्पोनेंट बनाते हैं।

// Card.js - एक सामान्य कंटेनर कॉम्पोनेंट
function Card(props) {
  return (
    <div className="card">
      {props.children}
    </div>
  );
}

// App.js - कार्ड कॉम्पोनेंट का उपयोग करना
function App() {
  return (
    <div>
      <Card>
        <h1>स्वागत है!</h1>
        <p>यह सामग्री एक कार्ड कॉम्पोनेंट के अंदर है।</p>
      </Card>

      <Card>
        <img src="/path/to/image.jpg" alt="एक उदाहरण छवि" />
        <p>यह एक इमेज कार्ड है।</p>
      </Card>
    </div>
  );
}

यहां, Card कॉम्पोनेंट को यह नहीं पता या परवाह नहीं है कि इसमें क्या है। यह केवल रैपर स्टाइलिंग प्रदान करता है। ओपनिंग और क्लोजिंग <Card> टैग के बीच की सामग्री स्वचालित रूप से props.children के रूप में पास हो जाती है। यह डिकपलिंग और पुन: प्रयोज्यता का एक सुंदर उदाहरण है।

तकनीक 2: प्रॉप्स के साथ स्पेशलाइजेशन

कभी-कभी, एक कॉम्पोनेंट को दूसरे कॉम्पोनेंट्स द्वारा भरे जाने के लिए कई 'होल' की आवश्यकता होती है। जबकि आप `props.children` का उपयोग कर सकते हैं, एक अधिक स्पष्ट और संरचित तरीका है कॉम्पोनेंट्स को नियमित प्रॉप्स के रूप में पास करना। इस पैटर्न को अक्सर स्पेशलाइजेशन कहा जाता है।

एक `Modal` कॉम्पोनेंट पर विचार करें। एक मोडल में आमतौर पर एक टाइटल सेक्शन, एक कंटेंट सेक्शन और एक एक्शन सेक्शन होता है (जिसमें "Confirm" या "Cancel" जैसे बटन होते हैं)। हम अपने `Modal` को इन सेक्शन्स को प्रॉप्स के रूप में स्वीकार करने के लिए डिज़ाइन कर सकते हैं।

// Modal.js - एक अधिक विशिष्ट कंटेनर
function Modal(props) {
  return (
    <div className="modal-backdrop">
      <div className="modal-content">
        <div className="modal-header">{props.title}</div>
        <div className="modal-body">{props.body}</div>
        <div className="modal-footer">{props.actions}</div>
      </div>
    </div>
  );
}

// App.js - विशिष्ट कॉम्पोनेंट्स के साथ मोडल का उपयोग करना
function App() {
  const confirmationTitle = <h2>कार्रवाई की पुष्टि करें</h2>;
  const confirmationBody = <p>क्या आप वाकई इस कार्रवाई के साथ आगे बढ़ना चाहते हैं?</p>;
  const confirmationActions = (
    <div>
      <button>पुष्टि करें</button>
      <button>रद्द करें</button>
    </div>
  );

  return (
    <Modal
      title={confirmationTitle}
      body={confirmationBody}
      actions={confirmationActions}
    />
  );
}

इस उदाहरण में, Modal एक अत्यधिक पुन: प्रयोज्य लेआउट कॉम्पोनेंट है। हम इसे इसके `title`, `body`, और `actions` के लिए विशिष्ट JSX तत्व पास करके विशेष बनाते हैं। यह `ConfirmationModal` और `WarningModal` सबक्लास बनाने से कहीं अधिक लचीला है। हम बस आवश्यकतानुसार `Modal` को विभिन्न सामग्री के साथ कंपोज करते हैं।

तकनीक 3: हायर-ऑर्डर कॉम्पोनेंट्स (HOCs)

गैर-UI लॉजिक, जैसे डेटा फ़ेचिंग, प्रमाणीकरण, या लॉगिंग साझा करने के लिए, रिएक्ट डेवलपर्स ऐतिहासिक रूप से हायर-ऑर्डर कॉम्पोनेंट्स (HOCs) नामक एक पैटर्न की ओर मुड़ते थे। हालांकि आधुनिक रिएक्ट में हुक्स द्वारा काफी हद तक प्रतिस्थापित कर दिया गया है, उन्हें समझना महत्वपूर्ण है क्योंकि वे रिएक्ट की कंपोजिशन कहानी में एक महत्वपूर्ण विकासवादी कदम का प्रतिनिधित्व करते हैं और अभी भी कई कोडबेस में मौजूद हैं।

एक HOC एक फ़ंक्शन है जो एक कॉम्पोनेंट को एक तर्क के रूप में लेता है और एक नया, उन्नत कॉम्पोनेंट लौटाता है।

आइए `withLogger` नामक एक HOC बनाएं जो किसी कॉम्पोनेंट के प्रॉप्स को जब भी अपडेट होता है, लॉग करता है। यह डीबगिंग के लिए उपयोगी है।

// withLogger.js - HOC
import React, { useEffect } from 'react';

function withLogger(WrappedComponent) {
  // यह एक नया कॉम्पोनेंट लौटाता है...
  return function EnhancedComponent(props) {
    useEffect(() => {
      console.log('Component updated with new props:', props);
    }, [props]);

    // ... जो मूल प्रॉप्स के साथ मूल कॉम्पोनेंट को रेंडर करता है।
    return <WrappedComponent {...props} />;
  };
}

// MyComponent.js - एक कॉम्पोनेंट जिसे उन्नत किया जाना है
function MyComponent({ name, age }) {
  return (
    <div>
      <h1>नमस्ते, {name}!</h1>
      <p>आप {age} वर्ष के हैं।</p>
    </div>
  );
}

// उन्नत कॉम्पोनेंट का निर्यात
export default withLogger(MyComponent);

`withLogger` फ़ंक्शन `MyComponent` को लपेटता है, इसे `MyComponent` के आंतरिक कोड को संशोधित किए बिना नई लॉगिंग क्षमताएं देता है। हम इसी HOC को किसी भी अन्य कॉम्पोनेंट पर लागू कर सकते हैं ताकि उसे वही लॉगिंग सुविधा मिल सके।

HOCs के साथ चुनौतियाँ:

तकनीक 4: रेंडर प्रॉप्स

रेंडर प्रॉप पैटर्न HOCs की कुछ कमियों के समाधान के रूप में उभरा। यह लॉजिक साझा करने का एक अधिक स्पष्ट तरीका प्रदान करता है।

एक रेंडर प्रॉप वाला कॉम्पोनेंट एक फ़ंक्शन को प्रॉप के रूप में लेता है (आमतौर पर `render` नाम दिया जाता है) और यह निर्धारित करने के लिए उस फ़ंक्शन को कॉल करता है कि क्या रेंडर करना है, किसी भी स्थिति या लॉजिक को तर्क के रूप में पास करता है।

आइए एक `MouseTracker` कॉम्पोनेंट बनाएं जो माउस के X और Y निर्देशांकों को ट्रैक करता है और उन्हें किसी भी कॉम्पोनेंट के लिए उपलब्ध कराता है जो उनका उपयोग करना चाहता है।

// MouseTracker.js - एक रेंडर प्रॉप वाला कॉम्पोनेंट
import React, { useState, useEffect } from 'react';

function MouseTracker({ render }) {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  const handleMouseMove = (event) => {
    setPosition({ x: event.clientX, y: event.clientY });
  };

  useEffect(() => {
    window.addEventListener('mousemove', handleMouseMove);
    return () => {
      window.removeEventListener('mousemove', handleMouseMove);
    };
  }, []);

  // स्थिति के साथ रेंडर फ़ंक्शन को कॉल करें
  return render(position);
}

// App.js - MouseTracker का उपयोग करना
function App() {
  return (
    <div>
      <h1>अपने माउस को चारों ओर घुमाएं!</h1>
      <MouseTracker
        render={mousePosition => (
          <p>वर्तमान माउस स्थिति है ({mousePosition.x}, {mousePosition.y})</p>
        )}
      />
    </div>
  );
}

यहां, `MouseTracker` माउस मूवमेंट को ट्रैक करने के लिए सभी लॉजिक को समाहित करता है। यह अपने आप कुछ भी रेंडर नहीं करता है। इसके बजाय, यह रेंडरिंग लॉजिक को अपने `render` प्रॉप को सौंपता है। यह HOCs से अधिक स्पष्ट है क्योंकि आप देख सकते हैं कि `mousePosition` डेटा ठीक JSX के अंदर से कहाँ आ रहा है।

`children` प्रॉप को एक फ़ंक्शन के रूप में भी इस्तेमाल किया जा सकता है, जो इस पैटर्न का एक सामान्य और सुंदर रूप है:

// children को एक फ़ंक्शन के रूप में उपयोग करना
<MouseTracker>
  {mousePosition => (
    <p>वर्तमान माउस स्थिति है ({mousePosition.x}, {mousePosition.y})</p>
  )}
</MouseTracker>

तकनीक 5: हुक्स (आधुनिक और पसंदीदा दृष्टिकोण)

रिएक्ट 16.8 में पेश किए गए, हुक्स ने हमारे रिएक्ट कॉम्पोनेंट्स लिखने के तरीके में क्रांति ला दी। वे आपको फंक्शनल कॉम्पोनेंट्स में स्टेट और अन्य रिएक्ट सुविधाओं का उपयोग करने की अनुमति देते हैं। सबसे महत्वपूर्ण बात, कस्टम हुक्स कॉम्पोनेंट्स के बीच स्टेटफुल लॉजिक साझा करने के लिए सबसे सुंदर और सीधा समाधान प्रदान करते हैं।

हुक्स HOCs और रेंडर प्रॉप्स की समस्याओं को बहुत साफ-सुथरे तरीके से हल करते हैं। आइए हमारे `MouseTracker` उदाहरण को `useMousePosition` नामक एक कस्टम हुक में रीफैक्टर करें।

// hooks/useMousePosition.js - एक कस्टम हुक
import { useState, useEffect } from 'react';

export function useMousePosition() {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const handleMouseMove = (event) => {
      setPosition({ x: event.clientX, y: event.clientY });
    };

    window.addEventListener('mousemove', handleMouseMove);
    return () => {
      window.removeEventListener('mousemove', handleMouseMove);
    };
  }, []); // खाली निर्भरता सरणी का मतलब है कि यह प्रभाव केवल एक बार चलता है

  return position;
}

// DisplayMousePosition.js - हुक का उपयोग करने वाला एक कॉम्पोनेंट
import { useMousePosition } from './hooks/useMousePosition';

function DisplayMousePosition() {
  const position = useMousePosition(); // बस हुक को कॉल करें!

  return (
    <p>
      माउस की स्थिति ({position.x}, {position.y}) है
    </p>
  );
}

// एक और कॉम्पोनेंट, शायद एक इंटरैक्टिव तत्व
import { useMousePosition } from './hooks/useMousePosition';

function InteractiveBox() {
  const { x, y } = useMousePosition();

  const style = {
    position: 'absolute',
    top: y - 25, // कर्सर पर बॉक्स को केंद्र में रखें
    left: x - 25,
    width: '50px',
    height: '50px',
    backgroundColor: 'lightblue',
  };

  return <div style={style} />;
}

यह एक बहुत बड़ा सुधार है। कोई 'रैपर हेल' नहीं है, कोई प्रॉप नेमिंग टकराव नहीं है, और कोई जटिल रेंडर प्रॉप फ़ंक्शन नहीं है। लॉजिक पूरी तरह से एक पुन: प्रयोज्य फ़ंक्शन (`useMousePosition`) में डिकपल हो गया है, और कोई भी कॉम्पोनेंट उस स्टेटफुल लॉजिक में कोड की एक ही, स्पष्ट पंक्ति के साथ 'हुक इन' कर सकता है। कस्टम हुक्स आधुनिक रिएक्ट में कंपोजिशन की अंतिम अभिव्यक्ति हैं, जो आपको पुन: प्रयोज्य लॉजिक ब्लॉक्स की अपनी लाइब्रेरी बनाने की अनुमति देते हैं।

एक त्वरित तुलना: रिएक्ट में कंपोजिशन बनाम इनहेरिटेंस

रिएक्ट के संदर्भ में मुख्य अंतरों को सारांशित करने के लिए, यहाँ एक सीधी तुलना है:

पहलू इनहेरिटेंस (रिएक्ट में एक एंटी-पैटर्न) कंपोजिशन (रिएक्ट में पसंदीदा)
संबंध 'is-a' (एक प्रकार का) संबंध। एक विशेष कॉम्पोनेंट एक बेस कॉम्पोनेंट का एक संस्करण है 'has-a' (पास है) या 'uses-a' (उपयोग करता है) संबंध। एक जटिल कॉम्पोनेंट के पास छोटे कॉम्पोनेंट होते हैं या वह साझा लॉजिक का उपयोग करता है
कपलिंग (Coupling) उच्च। चाइल्ड कॉम्पोनेंट अपने पैरेंट के कार्यान्वयन से कसकर जुड़े होते हैं। निम्न। कॉम्पोनेंट स्वतंत्र होते हैं और बिना किसी संशोधन के विभिन्न संदर्भों में पुन: उपयोग किए जा सकते हैं।
लचीलापन कम। कठोर, क्लास-आधारित पदानुक्रम विभिन्न कॉम्पोनेंट ट्री में लॉजिक साझा करना मुश्किल बनाते हैं। उच्च। लॉजिक और UI को अनगिनत तरीकों से जोड़ा और पुन: उपयोग किया जा सकता है, जैसे बिल्डिंग ब्लॉक्स।
कोड का पुन: उपयोग पूर्वनिर्धारित पदानुक्रम तक सीमित। जब आप सिर्फ "केला" चाहते हैं तो आपको पूरा "गोरिल्ला" मिलता है। उत्कृष्ट। छोटे, केंद्रित कॉम्पोनेंट्स और हुक्स का उपयोग पूरे एप्लिकेशन में किया जा सकता है।
रिएक्ट का तरीका आधिकारिक रिएक्ट टीम द्वारा हतोत्साहित किया गया। रिएक्ट एप्लिकेशन बनाने के लिए अनुशंसित और मुहावरेदार दृष्टिकोण।

निष्कर्ष: कंपोजिशन में सोचें

कंपोजिशन और इनहेरिटेंस के बीच बहस सॉफ्टवेयर डिजाइन में एक मूलभूत विषय है। जबकि इनहेरिटेंस का क्लासिकल OOP में अपना स्थान है, UI डेवलपमेंट की गतिशील, कॉम्पोनेंट-आधारित प्रकृति इसे रिएक्ट के लिए एक खराब फिट बनाती है। लाइब्रेरी को मौलिक रूप से कंपोजिशन को अपनाने के लिए डिज़ाइन किया गया था।

कंपोजिशन का पक्ष लेकर, आप प्राप्त करते हैं:

एक वैश्विक रिएक्ट डेवलपर के रूप में, कंपोजिशन में महारत हासिल करना केवल सर्वोत्तम प्रथाओं का पालन करने के बारे में नहीं है - यह उस मूल दर्शन को समझने के बारे में है जो रिएक्ट को इतना शक्तिशाली और उत्पादक उपकरण बनाता है। छोटे, केंद्रित कॉम्पोनेंट बनाकर शुरुआत करें। सामान्य कंटेनरों के लिए `props.children` और स्पेशलाइजेशन के लिए प्रॉप्स का उपयोग करें। लॉजिक साझा करने के लिए, पहले कस्टम हुक्स का उपयोग करें। कंपोजिशन में सोचकर, आप सुंदर, मजबूत और स्केलेबल रिएक्ट एप्लिकेशन बनाने की राह पर अच्छी तरह से होंगे जो समय की कसौटी पर खरे उतरते हैं।