← Back to React Course | Chapter 6: Custom Hooks & Advanced Patterns | Lesson 9 of 10

Compound Component Pattern क्या है

Compound components एक TV remote और TV जैसे हैं जो अलग-अलग बेचे जाते हैं लेकिन एक system के रूप में साथ perfectly काम करने के लिए designed हैं।
Syntax
markup
const ParentContext = createContext();

function Parent({ children }) {
  const [state, setState] = useState(initial);
  return (
    <ParentContext.Provider value={{ state, setState }}>
      {children}
    </ParentContext.Provider>
  );
}
Parent.Child = function Child() {
  const { state } = useContext(ParentContext);
  return <div>{state}</div>;
};

Components को 'Compound' क्या बनाता है

Compound components कई components हैं जिन्हें एक set के रूप में साथ उपयोग करने का मतलब है, जैसे HTML का <select> और <option>। एक huge configuration prop accept करने वाले एक component के बजाय, API को छोटे pieces में तोड़ा जाता है जो shared context के through implicitly communicate करते हैं, caller को ज़्यादा layout freedom देते हुए।

Note: एक अच्छा sign कि आपको compound components चाहिए: एक single component की prop list लंबी और awkward होती जा रही है (items, renderItem, itemClassName, ...)।
Warning: Compound components सिर्फ तब सही काम करते हैं जब pieces अपने intended parent के अंदर render हों — Tabs.Tab को <Tabs> के बाहर उपयोग करने पर connect होने के लिए कोई shared state नहीं है।

उदाहरण: What Makes Components 'Compound'

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
const TabsContext = React.createContext();
function Tabs({ children, defaultTab }) {
  const [active, setActive] = React.useState(defaultTab);
  return <TabsContext.Provider value={{ active, setActive }}>{children}</TabsContext.Provider>;
}
function Tab({ id, children }) {
  const { active, setActive } = React.useContext(TabsContext);
  return <button onClick={() => setActive(id)} style={{fontWeight: active === id ? "bold" : "normal"}}>{children}</button>;
}
function App() {
  return <Tabs defaultTab="a"><Tab id="a">Tab A</Tab><Tab id="b">Tab B</Tab></Tabs>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
  </script>
</body>
</html>

Props के बजाय Context के through State Share करना

हर level पर explicit props के through active tab पास करने के बजाय, compound components shared state को parent द्वारा provided एक Context में store करते हैं।

कोई भी sub-component फिर सीधे उस context को पढ़ सकता है, चाहे यह parent के children के अंदर कितना भी deep nested हो।

Note: Context object को component definitions के बाहर (module scope पर) बनाएं ताकि यह हर render पर recreate न हो।
Warning: अगर आप एक missing context check करना भूल जाएं (जैसे Tabs parent के बिना उपयोग किया गया Tab), useContext undefined return करता है और इसे destructure करना एक confusing error throw करता है।

उदाहरण: Sharing State via Context Instead of Props

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
const PanelContext = React.createContext();
function Accordion({ children }) {
  const [openIndex, setOpenIndex] = React.useState(null);
  return <PanelContext.Provider value={{ openIndex, setOpenIndex }}>{children}</PanelContext.Provider>;
}
function Panel({ index, title, children }) {
  const { openIndex, setOpenIndex } = React.useContext(PanelContext);
  const isOpen = openIndex === index;
  return <div><button onClick={() => setOpenIndex(isOpen ? null : index)}>{title}</button>{isOpen && <p>{children}</p>}</div>;
}
function App() {
  return <Accordion><Panel index={0} title="Section 1">Content 1</Panel><Panel index={1} title="Section 2">Content 2</Panel></Accordion>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
  </script>
</body>
</html>

एक Clean API के लिए Sub-Components Attach करना

एक common finishing touch sub-components को main component की properties के रूप में attach करना है, जैसे Tabs.Tab = Tab। यह consumers को सिर्फ एक नाम (Tabs) import करने देता है और इसके through सब कुछ access करने देता है, related pieces को obviously connected बनाते हुए।

Note: यह बिल्कुल एक ergonomic/discoverability improvement है — Tabs.Tab और एक अलग से exported Tab function runtime पर identically behave करते हैं।
Warning: Define करने के बाद sub-components attach करना (Tabs.Tab = Tab) दोनों Tabs और Tab declare होने के बाद ही होना चाहिए, नहीं तो आप undefined assign करेंगे।

उदाहरण: Attaching Sub-Components for a Clean API

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
const TabsContext = React.createContext();
function Tabs({ children, defaultTab }) {
  const [active, setActive] = React.useState(defaultTab);
  return <TabsContext.Provider value={{ active, setActive }}>{children}</TabsContext.Provider>;
}
function Tab({ id, children }) {
  const { active, setActive } = React.useContext(TabsContext);
  return <button onClick={() => setActive(id)} style={{fontWeight: active === id ? "bold" : "normal"}}>{children}</button>;
}
Tabs.Tab = Tab;
function App() {
  return <Tabs defaultTab="x"><Tabs.Tab id="x">X</Tabs.Tab><Tabs.Tab id="y">Y</Tabs.Tab></Tabs>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
  </script>
</body>
</html>
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
आम गलतियां
  1. Context के through shared state setup किए बिना compound components उपयोग करना, pieces के बीच awkward prop-drilling मजबूर करते हुए।
  2. Sub-components को parent की properties के रूप में export न करना (जैसे Tabs.Panel), API को कम discoverable बनाते हुए।
  3. Sub-components को इनके parent के बाहर उपयोग होने देना, जहां इनके पास कोई shared context नहीं है और चुपचाप fail होते हैं।
चैप्टर सारांश
  • Compound components components का एक set हैं जो साथ काम करने के लिए designed हैं, Context के through implicit state share करते हुए।
  • Classic example एक Tabs component है जो Tabs, Tabs.List, Tabs.Tab, और Tabs.Panel से मिलकर साथ काम करता है।
  • यह pattern consumer को layout पर flexible control देता है जबकि parent internally shared state manage करता है।
  • यह आमतौर पर React Context उपयोग करके implement होता है state को manual prop drilling के बिना नीचे pass करने के लिए।
ब्राउज़र सपोर्ट

React 16.3 (stable Context API) से available है — पहले की versions इसे React.Children/cloneElement से approximate कर सकती थीं।

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.