[{"data":1,"prerenderedAt":5953},["ShallowReactive",2],{"page-\u002Freact\u002F15-composition-vs-inheritance":3},{"id":4,"title":5,"body":6,"description":5946,"extension":5947,"meta":5948,"navigation":64,"path":5949,"seo":5950,"stem":5951,"__hash__":5952},"content\u002Freact\u002F15-composition-vs-inheritance.md","15 — Composition vs. Inheritance",{"type":7,"value":8,"toc":5919},"minimark",[9,13,18,148,156,342,346,569,574,886,890,1170,1174,1944,1948,2230,2237,2627,2631,3329,3336,3563,3567,3747,3751,4213,4217,4475,4479,4979,4983,5135,5139,5325,5329,5344,5619,5779,5783,5915],[10,11,5],"h1",{"id":12},"_15-composition-vs-inheritance",[14,15,17],"h2",{"id":16},"why-react-has-no-component-inheritance","Why React Has No Component Inheritance",[19,20,23],"code-wrapper",{"filename":21,"language":22},"no_inheritance.js","javascript",[24,25,29],"pre",{"className":26,"code":27,"language":22,"meta":28,"style":28},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F React deliberately omits class-style component inheritance. There is no\n\u002F\u002F `class SpecialButton extends Button` equivalent, and the React team has\n\u002F\u002F consistently stated they have never found a use case where `extends` produces\n\u002F\u002F a better result than composition.\n\n\u002F\u002F STRUCTURAL REASON: inheritance couples a subclass to its parent's INTERNAL\n\u002F\u002F implementation — which methods are overridable, what super.render() returns,\n\u002F\u002F which internal state is shared. Composition couples components only through\n\u002F\u002F EXPLICIT, VISIBLE props — a much shallower, inspectable form of coupling.\n\n\u002F\u002F What OO would write as inheritance, React expresses as:\n\u002F\u002F   1. Passing JSX through `children` (single slot) or named props (multi-slot)\n\u002F\u002F   2. Specialization via configuration props + thin wrapper functions\n\u002F\u002F   3. Shared implicit state via Context (compound components)\n\u002F\u002F   4. Shared logic via custom hooks (chapter 11)\n\n\u002F\u002F This is NOT a missing feature — it's a design decision: the component tree\n\u002F\u002F is composed, not inherited. Every reuse case OO solves with inheritance,\n\u002F\u002F React solves with one of the composition patterns below.\n","",[30,31,32,41,47,53,59,66,72,78,84,90,95,101,107,113,119,125,130,136,142],"code",{"__ignoreMap":28},[33,34,37],"span",{"class":35,"line":36},"line",1,[33,38,40],{"class":39},"sdCPZ","\u002F\u002F React deliberately omits class-style component inheritance. There is no\n",[33,42,44],{"class":35,"line":43},2,[33,45,46],{"class":39},"\u002F\u002F `class SpecialButton extends Button` equivalent, and the React team has\n",[33,48,50],{"class":35,"line":49},3,[33,51,52],{"class":39},"\u002F\u002F consistently stated they have never found a use case where `extends` produces\n",[33,54,56],{"class":35,"line":55},4,[33,57,58],{"class":39},"\u002F\u002F a better result than composition.\n",[33,60,62],{"class":35,"line":61},5,[33,63,65],{"emptyLinePlaceholder":64},true,"\n",[33,67,69],{"class":35,"line":68},6,[33,70,71],{"class":39},"\u002F\u002F STRUCTURAL REASON: inheritance couples a subclass to its parent's INTERNAL\n",[33,73,75],{"class":35,"line":74},7,[33,76,77],{"class":39},"\u002F\u002F implementation — which methods are overridable, what super.render() returns,\n",[33,79,81],{"class":35,"line":80},8,[33,82,83],{"class":39},"\u002F\u002F which internal state is shared. Composition couples components only through\n",[33,85,87],{"class":35,"line":86},9,[33,88,89],{"class":39},"\u002F\u002F EXPLICIT, VISIBLE props — a much shallower, inspectable form of coupling.\n",[33,91,93],{"class":35,"line":92},10,[33,94,65],{"emptyLinePlaceholder":64},[33,96,98],{"class":35,"line":97},11,[33,99,100],{"class":39},"\u002F\u002F What OO would write as inheritance, React expresses as:\n",[33,102,104],{"class":35,"line":103},12,[33,105,106],{"class":39},"\u002F\u002F   1. Passing JSX through `children` (single slot) or named props (multi-slot)\n",[33,108,110],{"class":35,"line":109},13,[33,111,112],{"class":39},"\u002F\u002F   2. Specialization via configuration props + thin wrapper functions\n",[33,114,116],{"class":35,"line":115},14,[33,117,118],{"class":39},"\u002F\u002F   3. Shared implicit state via Context (compound components)\n",[33,120,122],{"class":35,"line":121},15,[33,123,124],{"class":39},"\u002F\u002F   4. Shared logic via custom hooks (chapter 11)\n",[33,126,128],{"class":35,"line":127},16,[33,129,65],{"emptyLinePlaceholder":64},[33,131,133],{"class":35,"line":132},17,[33,134,135],{"class":39},"\u002F\u002F This is NOT a missing feature — it's a design decision: the component tree\n",[33,137,139],{"class":35,"line":138},18,[33,140,141],{"class":39},"\u002F\u002F is composed, not inherited. Every reuse case OO solves with inheritance,\n",[33,143,145],{"class":35,"line":144},19,[33,146,147],{"class":39},"\u002F\u002F React solves with one of the composition patterns below.\n",[14,149,151,152,155],{"id":150},"the-children-prop-single-slot-composition","The ",[30,153,154],{},"children"," Prop: Single-Slot Composition",[19,157,159],{"filename":158,"language":22},"children_prop.js",[24,160,162],{"className":26,"code":161,"language":22,"meta":28,"style":28},"\u002F\u002F `children` is the simplest composition primitive: a wrapper component receives\n\u002F\u002F arbitrary JSX through its `children` prop and controls layout\u002Fbehavior while\n\u002F\u002F remaining agnostic about what's inside.\n\nfunction Card({ children }) {\n  return \u003Cdiv className=\"card\">{children}\u003C\u002Fdiv>\n}\n\n\u002F\u002F Card never needs to know it's rendering a heading and a paragraph — it could\n\u002F\u002F wrap a chart, a form, or another Card. This replaces what `SpecialButton\n\u002F\u002F extends Button` would attempt: reusing Card's behavior across arbitrarily\n\u002F\u002F different content, without a class hierarchy.\n\nfunction Dashboard() {\n  return (\n    \u003CCard>\n      \u003Ch2>Revenue\u003C\u002Fh2>\n      \u003Cp>$42,000 this month\u003C\u002Fp>\n    \u003C\u002FCard>\n  )\n}\n",[30,163,164,169,174,179,183,203,233,238,242,247,252,257,262,266,276,283,294,308,322,331,337],{"__ignoreMap":28},[33,165,166],{"class":35,"line":36},[33,167,168],{"class":39},"\u002F\u002F `children` is the simplest composition primitive: a wrapper component receives\n",[33,170,171],{"class":35,"line":43},[33,172,173],{"class":39},"\u002F\u002F arbitrary JSX through its `children` prop and controls layout\u002Fbehavior while\n",[33,175,176],{"class":35,"line":49},[33,177,178],{"class":39},"\u002F\u002F remaining agnostic about what's inside.\n",[33,180,181],{"class":35,"line":55},[33,182,65],{"emptyLinePlaceholder":64},[33,184,185,189,193,197,200],{"class":35,"line":61},[33,186,188],{"class":187},"svdQ7","function",[33,190,192],{"class":191},"sIsaT"," Card",[33,194,196],{"class":195},"ssxIu","({ ",[33,198,154],{"class":199},"sCrzJ",[33,201,202],{"class":195}," }) {\n",[33,204,205,208,211,215,218,221,225,228,230],{"class":35,"line":68},[33,206,207],{"class":187},"  return",[33,209,210],{"class":195}," \u003C",[33,212,214],{"class":213},"sk71V","div",[33,216,217],{"class":191}," className",[33,219,220],{"class":187},"=",[33,222,224],{"class":223},"sJ6F3","\"card\"",[33,226,227],{"class":195},">{children}\u003C\u002F",[33,229,214],{"class":213},[33,231,232],{"class":195},">\n",[33,234,235],{"class":35,"line":74},[33,236,237],{"class":195},"}\n",[33,239,240],{"class":35,"line":80},[33,241,65],{"emptyLinePlaceholder":64},[33,243,244],{"class":35,"line":86},[33,245,246],{"class":39},"\u002F\u002F Card never needs to know it's rendering a heading and a paragraph — it could\n",[33,248,249],{"class":35,"line":92},[33,250,251],{"class":39},"\u002F\u002F wrap a chart, a form, or another Card. This replaces what `SpecialButton\n",[33,253,254],{"class":35,"line":97},[33,255,256],{"class":39},"\u002F\u002F extends Button` would attempt: reusing Card's behavior across arbitrarily\n",[33,258,259],{"class":35,"line":103},[33,260,261],{"class":39},"\u002F\u002F different content, without a class hierarchy.\n",[33,263,264],{"class":35,"line":109},[33,265,65],{"emptyLinePlaceholder":64},[33,267,268,270,273],{"class":35,"line":115},[33,269,188],{"class":187},[33,271,272],{"class":191}," Dashboard",[33,274,275],{"class":195},"() {\n",[33,277,278,280],{"class":35,"line":121},[33,279,207],{"class":187},[33,281,282],{"class":195}," (\n",[33,284,285,288,292],{"class":35,"line":127},[33,286,287],{"class":195},"    \u003C",[33,289,291],{"class":290},"snvgF","Card",[33,293,232],{"class":195},[33,295,296,299,301,304,306],{"class":35,"line":132},[33,297,298],{"class":195},"      \u003C",[33,300,14],{"class":213},[33,302,303],{"class":195},">Revenue\u003C\u002F",[33,305,14],{"class":213},[33,307,232],{"class":195},[33,309,310,312,315,318,320],{"class":35,"line":138},[33,311,298],{"class":195},[33,313,314],{"class":213},"p",[33,316,317],{"class":195},">$42,000 this month\u003C\u002F",[33,319,314],{"class":213},[33,321,232],{"class":195},[33,323,324,327,329],{"class":35,"line":144},[33,325,326],{"class":195},"    \u003C\u002F",[33,328,291],{"class":290},[33,330,232],{"class":195},[33,332,334],{"class":35,"line":333},20,[33,335,336],{"class":195},"  )\n",[33,338,340],{"class":35,"line":339},21,[33,341,237],{"class":195},[14,343,345],{"id":344},"named-slots-multiple-independent-content-regions","Named Slots: Multiple Independent Content Regions",[19,347,349],{"filename":348,"language":22},"named_slots.js",[24,350,352],{"className":26,"code":351,"language":22,"meta":28,"style":28},"\u002F\u002F `children` handles ONE insertion point. When a component needs multiple\n\u002F\u002F independent regions (a sidebar layout with header, nav, and main content),\n\u002F\u002F pass JSX through multiple named props — the \"slots\" pattern.\n\nfunction SplitLayout({ sidebar, main }) {\n  return (\n    \u003Cdiv className=\"split-layout\">\n      \u003Caside className=\"sidebar\">{sidebar}\u003C\u002Faside>\n      \u003Cmain className=\"main\">{main}\u003C\u002Fmain>\n    \u003C\u002Fdiv>\n  )\n}\n\n\u002F\u002F SplitLayout controls ARRANGEMENT (which slot goes where, wrapped in which\n\u002F\u002F semantic elements) while delegating CONTENT to the caller. Inheritance would\n\u002F\u002F conflate \"controls layout\" and \"controls behavior\" into one `extends` chain;\n\u002F\u002F slots keep them cleanly separated.\n\nfunction App() {\n  return (\n    \u003CSplitLayout\n      sidebar={\u003CNavMenu \u002F>}\n      main={\u003CDashboard \u002F>}\n    \u002F>\n  )\n}\n",[30,353,354,359,364,369,373,393,399,414,435,455,463,467,471,475,480,485,490,495,499,508,514,521,538,553,559,564],{"__ignoreMap":28},[33,355,356],{"class":35,"line":36},[33,357,358],{"class":39},"\u002F\u002F `children` handles ONE insertion point. When a component needs multiple\n",[33,360,361],{"class":35,"line":43},[33,362,363],{"class":39},"\u002F\u002F independent regions (a sidebar layout with header, nav, and main content),\n",[33,365,366],{"class":35,"line":49},[33,367,368],{"class":39},"\u002F\u002F pass JSX through multiple named props — the \"slots\" pattern.\n",[33,370,371],{"class":35,"line":55},[33,372,65],{"emptyLinePlaceholder":64},[33,374,375,377,380,382,385,388,391],{"class":35,"line":61},[33,376,188],{"class":187},[33,378,379],{"class":191}," SplitLayout",[33,381,196],{"class":195},[33,383,384],{"class":199},"sidebar",[33,386,387],{"class":195},", ",[33,389,390],{"class":199},"main",[33,392,202],{"class":195},[33,394,395,397],{"class":35,"line":68},[33,396,207],{"class":187},[33,398,282],{"class":195},[33,400,401,403,405,407,409,412],{"class":35,"line":74},[33,402,287],{"class":195},[33,404,214],{"class":213},[33,406,217],{"class":191},[33,408,220],{"class":187},[33,410,411],{"class":223},"\"split-layout\"",[33,413,232],{"class":195},[33,415,416,418,421,423,425,428,431,433],{"class":35,"line":80},[33,417,298],{"class":195},[33,419,420],{"class":213},"aside",[33,422,217],{"class":191},[33,424,220],{"class":187},[33,426,427],{"class":223},"\"sidebar\"",[33,429,430],{"class":195},">{sidebar}\u003C\u002F",[33,432,420],{"class":213},[33,434,232],{"class":195},[33,436,437,439,441,443,445,448,451,453],{"class":35,"line":86},[33,438,298],{"class":195},[33,440,390],{"class":213},[33,442,217],{"class":191},[33,444,220],{"class":187},[33,446,447],{"class":223},"\"main\"",[33,449,450],{"class":195},">{main}\u003C\u002F",[33,452,390],{"class":213},[33,454,232],{"class":195},[33,456,457,459,461],{"class":35,"line":92},[33,458,326],{"class":195},[33,460,214],{"class":213},[33,462,232],{"class":195},[33,464,465],{"class":35,"line":97},[33,466,336],{"class":195},[33,468,469],{"class":35,"line":103},[33,470,237],{"class":195},[33,472,473],{"class":35,"line":109},[33,474,65],{"emptyLinePlaceholder":64},[33,476,477],{"class":35,"line":115},[33,478,479],{"class":39},"\u002F\u002F SplitLayout controls ARRANGEMENT (which slot goes where, wrapped in which\n",[33,481,482],{"class":35,"line":121},[33,483,484],{"class":39},"\u002F\u002F semantic elements) while delegating CONTENT to the caller. Inheritance would\n",[33,486,487],{"class":35,"line":127},[33,488,489],{"class":39},"\u002F\u002F conflate \"controls layout\" and \"controls behavior\" into one `extends` chain;\n",[33,491,492],{"class":35,"line":132},[33,493,494],{"class":39},"\u002F\u002F slots keep them cleanly separated.\n",[33,496,497],{"class":35,"line":138},[33,498,65],{"emptyLinePlaceholder":64},[33,500,501,503,506],{"class":35,"line":144},[33,502,188],{"class":187},[33,504,505],{"class":191}," App",[33,507,275],{"class":195},[33,509,510,512],{"class":35,"line":333},[33,511,207],{"class":187},[33,513,282],{"class":195},[33,515,516,518],{"class":35,"line":339},[33,517,287],{"class":195},[33,519,520],{"class":290},"SplitLayout\n",[33,522,524,527,529,532,535],{"class":35,"line":523},22,[33,525,526],{"class":191},"      sidebar",[33,528,220],{"class":187},[33,530,531],{"class":195},"{\u003C",[33,533,534],{"class":290},"NavMenu",[33,536,537],{"class":195}," \u002F>}\n",[33,539,541,544,546,548,551],{"class":35,"line":540},23,[33,542,543],{"class":191},"      main",[33,545,220],{"class":187},[33,547,531],{"class":195},[33,549,550],{"class":290},"Dashboard",[33,552,537],{"class":195},[33,554,556],{"class":35,"line":555},24,[33,557,558],{"class":195},"    \u002F>\n",[33,560,562],{"class":35,"line":561},25,[33,563,336],{"class":195},[33,565,567],{"class":35,"line":566},26,[33,568,237],{"class":195},[570,571,573],"h3",{"id":572},"anti-pattern-over-slotting","Anti-Pattern: Over-Slotting",[19,575,577],{"filename":576,"language":22},"over_slotting.js",[24,578,580],{"className":26,"code":579,"language":22,"meta":28,"style":28},"\u002F\u002F ANTI-PATTERN: when a component accumulates so many slots that the call site\n\u002F\u002F becomes unreadable, you've outgrown the slot pattern — refactor to compound\n\u002F\u002F components (below) or split into smaller components.\n\nfunction UserCard({ avatarSlot, nameSlot, bioSlot, statsSlot, actionsSlot, footerSlot }) {\n  \u002F\u002F 6 slots = the caller's JSX becomes a wall of prop assignments, each holding JSX.\n  \u002F\u002F This is a signal that these pieces are RELATED and should share implicit state.\n  return (\n    \u003Cdiv className=\"user-card\">\n      \u003Cdiv className=\"header\">{avatarSlot}{nameSlot}\u003C\u002Fdiv>\n      \u003Cdiv className=\"body\">{bioSlot}{statsSlot}\u003C\u002Fdiv>\n      \u003Cdiv className=\"actions\">{actionsSlot}\u003C\u002Fdiv>\n      \u003Cdiv className=\"footer\">{footerSlot}\u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  )\n}\n\n\u002F\u002F BAD: 6-slot usage — hard to read, no shared state, just prop wiring\n;\u003CUserCard\n  avatarSlot={\u003CAvatar user={user} \u002F>}\n  nameSlot={\u003Ch3>{user.name}\u003C\u002Fh3>}\n  bioSlot={\u003Cp>{user.bio}\u003C\u002Fp>}\n  statsSlot={\u003CUserStats user={user} \u002F>}\n  actionsSlot={\u003CFollowButton user={user} \u002F>}\n  footerSlot={\u003CUserFooter user={user} \u002F>}\n\u002F>\n",[30,581,582,587,592,597,601,640,645,650,656,671,691,711,731,751,759,763,767,771,776,787,807,817,827,845,863,881],{"__ignoreMap":28},[33,583,584],{"class":35,"line":36},[33,585,586],{"class":39},"\u002F\u002F ANTI-PATTERN: when a component accumulates so many slots that the call site\n",[33,588,589],{"class":35,"line":43},[33,590,591],{"class":39},"\u002F\u002F becomes unreadable, you've outgrown the slot pattern — refactor to compound\n",[33,593,594],{"class":35,"line":49},[33,595,596],{"class":39},"\u002F\u002F components (below) or split into smaller components.\n",[33,598,599],{"class":35,"line":55},[33,600,65],{"emptyLinePlaceholder":64},[33,602,603,605,608,610,613,615,618,620,623,625,628,630,633,635,638],{"class":35,"line":61},[33,604,188],{"class":187},[33,606,607],{"class":191}," UserCard",[33,609,196],{"class":195},[33,611,612],{"class":199},"avatarSlot",[33,614,387],{"class":195},[33,616,617],{"class":199},"nameSlot",[33,619,387],{"class":195},[33,621,622],{"class":199},"bioSlot",[33,624,387],{"class":195},[33,626,627],{"class":199},"statsSlot",[33,629,387],{"class":195},[33,631,632],{"class":199},"actionsSlot",[33,634,387],{"class":195},[33,636,637],{"class":199},"footerSlot",[33,639,202],{"class":195},[33,641,642],{"class":35,"line":68},[33,643,644],{"class":39},"  \u002F\u002F 6 slots = the caller's JSX becomes a wall of prop assignments, each holding JSX.\n",[33,646,647],{"class":35,"line":74},[33,648,649],{"class":39},"  \u002F\u002F This is a signal that these pieces are RELATED and should share implicit state.\n",[33,651,652,654],{"class":35,"line":80},[33,653,207],{"class":187},[33,655,282],{"class":195},[33,657,658,660,662,664,666,669],{"class":35,"line":86},[33,659,287],{"class":195},[33,661,214],{"class":213},[33,663,217],{"class":191},[33,665,220],{"class":187},[33,667,668],{"class":223},"\"user-card\"",[33,670,232],{"class":195},[33,672,673,675,677,679,681,684,687,689],{"class":35,"line":92},[33,674,298],{"class":195},[33,676,214],{"class":213},[33,678,217],{"class":191},[33,680,220],{"class":187},[33,682,683],{"class":223},"\"header\"",[33,685,686],{"class":195},">{avatarSlot}{nameSlot}\u003C\u002F",[33,688,214],{"class":213},[33,690,232],{"class":195},[33,692,693,695,697,699,701,704,707,709],{"class":35,"line":97},[33,694,298],{"class":195},[33,696,214],{"class":213},[33,698,217],{"class":191},[33,700,220],{"class":187},[33,702,703],{"class":223},"\"body\"",[33,705,706],{"class":195},">{bioSlot}{statsSlot}\u003C\u002F",[33,708,214],{"class":213},[33,710,232],{"class":195},[33,712,713,715,717,719,721,724,727,729],{"class":35,"line":103},[33,714,298],{"class":195},[33,716,214],{"class":213},[33,718,217],{"class":191},[33,720,220],{"class":187},[33,722,723],{"class":223},"\"actions\"",[33,725,726],{"class":195},">{actionsSlot}\u003C\u002F",[33,728,214],{"class":213},[33,730,232],{"class":195},[33,732,733,735,737,739,741,744,747,749],{"class":35,"line":109},[33,734,298],{"class":195},[33,736,214],{"class":213},[33,738,217],{"class":191},[33,740,220],{"class":187},[33,742,743],{"class":223},"\"footer\"",[33,745,746],{"class":195},">{footerSlot}\u003C\u002F",[33,748,214],{"class":213},[33,750,232],{"class":195},[33,752,753,755,757],{"class":35,"line":115},[33,754,326],{"class":195},[33,756,214],{"class":213},[33,758,232],{"class":195},[33,760,761],{"class":35,"line":121},[33,762,336],{"class":195},[33,764,765],{"class":35,"line":127},[33,766,237],{"class":195},[33,768,769],{"class":35,"line":132},[33,770,65],{"emptyLinePlaceholder":64},[33,772,773],{"class":35,"line":138},[33,774,775],{"class":39},"\u002F\u002F BAD: 6-slot usage — hard to read, no shared state, just prop wiring\n",[33,777,778,781,784],{"class":35,"line":144},[33,779,780],{"class":195},";",[33,782,783],{"class":187},"\u003C",[33,785,786],{"class":195},"UserCard\n",[33,788,789,792,794,797,799,802,805],{"class":35,"line":333},[33,790,791],{"class":195},"  avatarSlot",[33,793,220],{"class":187},[33,795,796],{"class":195},"{\u003CAvatar user",[33,798,220],{"class":187},[33,800,801],{"class":195},"{user} ",[33,803,804],{"class":187},"\u002F>",[33,806,237],{"class":195},[33,808,809,812,814],{"class":35,"line":339},[33,810,811],{"class":195},"  nameSlot",[33,813,220],{"class":187},[33,815,816],{"class":195},"{\u003Ch3>{user.name}\u003C\u002Fh3>}\n",[33,818,819,822,824],{"class":35,"line":523},[33,820,821],{"class":195},"  bioSlot",[33,823,220],{"class":187},[33,825,826],{"class":195},"{\u003Cp>{user.bio}\u003C\u002Fp>}\n",[33,828,829,832,834,837,839,841,843],{"class":35,"line":540},[33,830,831],{"class":195},"  statsSlot",[33,833,220],{"class":187},[33,835,836],{"class":195},"{\u003CUserStats user",[33,838,220],{"class":187},[33,840,801],{"class":195},[33,842,804],{"class":187},[33,844,237],{"class":195},[33,846,847,850,852,855,857,859,861],{"class":35,"line":555},[33,848,849],{"class":195},"  actionsSlot",[33,851,220],{"class":187},[33,853,854],{"class":195},"{\u003CFollowButton user",[33,856,220],{"class":187},[33,858,801],{"class":195},[33,860,804],{"class":187},[33,862,237],{"class":195},[33,864,865,868,870,873,875,877,879],{"class":35,"line":561},[33,866,867],{"class":195},"  footerSlot",[33,869,220],{"class":187},[33,871,872],{"class":195},"{\u003CUserFooter user",[33,874,220],{"class":187},[33,876,801],{"class":195},[33,878,804],{"class":187},[33,880,237],{"class":195},[33,882,883],{"class":35,"line":566},[33,884,885],{"class":187},"\u002F>\n",[14,887,889],{"id":888},"specialization-through-configuration-not-subclassing","Specialization Through Configuration, Not Subclassing",[19,891,893],{"filename":892,"language":22},"specialization.js",[24,894,896],{"className":26,"code":895,"language":22,"meta":28,"style":28},"\u002F\u002F OO would create `PrimaryButton extends Button` and `DangerButton extends Button`.\n\u002F\u002F React expresses \"specialized variant of a general thing\" by having the general\n\u002F\u002F component accept configuration props, and optionally wrapping it in a thin\n\u002F\u002F named component that supplies defaults.\n\nfunction Button({ variant = 'default', size = 'md', children, ...props }) {\n  const className = `btn btn-${variant} btn-${size}`\n  return \u003Cbutton className={className} {...props}>{children}\u003C\u002Fbutton>\n}\n\n\u002F\u002F \"Specialization\" = composition + default props, NOT a subclass.\n\u002F\u002F DangerButton forwards ALL props and overrides only `variant`. Any future prop\n\u002F\u002F Button gains (e.g. `loading`) is automatically available on DangerButton too,\n\u002F\u002F with zero changes to DangerButton — the \"free inheritance of new capability\"\n\u002F\u002F OO promises, obtained through prop forwarding instead.\n\nfunction DangerButton(props) {\n  return \u003CButton variant=\"danger\" {...props} \u002F>\n}\n\nfunction App() {\n  return (\n    \u003C>\n      \u003CButton>Save\u003C\u002FButton>\n      \u003CDangerButton size=\"lg\" onClick={handleDelete}>Delete Account\u003C\u002FDangerButton>\n    \u003C\u002F>\n  )\n}\n",[30,897,898,903,908,913,918,922,964,986,1011,1015,1019,1024,1029,1034,1039,1044,1048,1063,1088,1092,1096,1104,1110,1115,1128,1155,1160,1165],{"__ignoreMap":28},[33,899,900],{"class":35,"line":36},[33,901,902],{"class":39},"\u002F\u002F OO would create `PrimaryButton extends Button` and `DangerButton extends Button`.\n",[33,904,905],{"class":35,"line":43},[33,906,907],{"class":39},"\u002F\u002F React expresses \"specialized variant of a general thing\" by having the general\n",[33,909,910],{"class":35,"line":49},[33,911,912],{"class":39},"\u002F\u002F component accept configuration props, and optionally wrapping it in a thin\n",[33,914,915],{"class":35,"line":55},[33,916,917],{"class":39},"\u002F\u002F named component that supplies defaults.\n",[33,919,920],{"class":35,"line":61},[33,921,65],{"emptyLinePlaceholder":64},[33,923,924,926,929,931,934,937,940,942,945,947,950,952,954,956,959,962],{"class":35,"line":68},[33,925,188],{"class":187},[33,927,928],{"class":191}," Button",[33,930,196],{"class":195},[33,932,933],{"class":199},"variant",[33,935,936],{"class":187}," =",[33,938,939],{"class":223}," 'default'",[33,941,387],{"class":195},[33,943,944],{"class":199},"size",[33,946,936],{"class":187},[33,948,949],{"class":223}," 'md'",[33,951,387],{"class":195},[33,953,154],{"class":199},[33,955,387],{"class":195},[33,957,958],{"class":187},"...",[33,960,961],{"class":199},"props",[33,963,202],{"class":195},[33,965,966,969,971,973,976,978,981,983],{"class":35,"line":74},[33,967,968],{"class":187},"  const",[33,970,217],{"class":290},[33,972,936],{"class":187},[33,974,975],{"class":223}," `btn btn-${",[33,977,933],{"class":195},[33,979,980],{"class":223},"} btn-${",[33,982,944],{"class":195},[33,984,985],{"class":223},"}`\n",[33,987,988,990,992,995,997,999,1002,1004,1007,1009],{"class":35,"line":80},[33,989,207],{"class":187},[33,991,210],{"class":195},[33,993,994],{"class":213},"button",[33,996,217],{"class":191},[33,998,220],{"class":187},[33,1000,1001],{"class":195},"{className} {",[33,1003,958],{"class":187},[33,1005,1006],{"class":195},"props}>{children}\u003C\u002F",[33,1008,994],{"class":213},[33,1010,232],{"class":195},[33,1012,1013],{"class":35,"line":86},[33,1014,237],{"class":195},[33,1016,1017],{"class":35,"line":92},[33,1018,65],{"emptyLinePlaceholder":64},[33,1020,1021],{"class":35,"line":97},[33,1022,1023],{"class":39},"\u002F\u002F \"Specialization\" = composition + default props, NOT a subclass.\n",[33,1025,1026],{"class":35,"line":103},[33,1027,1028],{"class":39},"\u002F\u002F DangerButton forwards ALL props and overrides only `variant`. Any future prop\n",[33,1030,1031],{"class":35,"line":109},[33,1032,1033],{"class":39},"\u002F\u002F Button gains (e.g. `loading`) is automatically available on DangerButton too,\n",[33,1035,1036],{"class":35,"line":115},[33,1037,1038],{"class":39},"\u002F\u002F with zero changes to DangerButton — the \"free inheritance of new capability\"\n",[33,1040,1041],{"class":35,"line":121},[33,1042,1043],{"class":39},"\u002F\u002F OO promises, obtained through prop forwarding instead.\n",[33,1045,1046],{"class":35,"line":127},[33,1047,65],{"emptyLinePlaceholder":64},[33,1049,1050,1052,1055,1058,1060],{"class":35,"line":132},[33,1051,188],{"class":187},[33,1053,1054],{"class":191}," DangerButton",[33,1056,1057],{"class":195},"(",[33,1059,961],{"class":199},[33,1061,1062],{"class":195},") {\n",[33,1064,1065,1067,1069,1072,1075,1077,1080,1083,1085],{"class":35,"line":138},[33,1066,207],{"class":187},[33,1068,210],{"class":195},[33,1070,1071],{"class":290},"Button",[33,1073,1074],{"class":191}," variant",[33,1076,220],{"class":187},[33,1078,1079],{"class":223},"\"danger\"",[33,1081,1082],{"class":195}," {",[33,1084,958],{"class":187},[33,1086,1087],{"class":195},"props} \u002F>\n",[33,1089,1090],{"class":35,"line":144},[33,1091,237],{"class":195},[33,1093,1094],{"class":35,"line":333},[33,1095,65],{"emptyLinePlaceholder":64},[33,1097,1098,1100,1102],{"class":35,"line":339},[33,1099,188],{"class":187},[33,1101,505],{"class":191},[33,1103,275],{"class":195},[33,1105,1106,1108],{"class":35,"line":523},[33,1107,207],{"class":187},[33,1109,282],{"class":195},[33,1111,1112],{"class":35,"line":540},[33,1113,1114],{"class":195},"    \u003C>\n",[33,1116,1117,1119,1121,1124,1126],{"class":35,"line":555},[33,1118,298],{"class":195},[33,1120,1071],{"class":290},[33,1122,1123],{"class":195},">Save\u003C\u002F",[33,1125,1071],{"class":290},[33,1127,232],{"class":195},[33,1129,1130,1132,1135,1138,1140,1143,1146,1148,1151,1153],{"class":35,"line":561},[33,1131,298],{"class":195},[33,1133,1134],{"class":290},"DangerButton",[33,1136,1137],{"class":191}," size",[33,1139,220],{"class":187},[33,1141,1142],{"class":223},"\"lg\"",[33,1144,1145],{"class":191}," onClick",[33,1147,220],{"class":187},[33,1149,1150],{"class":195},"{handleDelete}>Delete Account\u003C\u002F",[33,1152,1134],{"class":290},[33,1154,232],{"class":195},[33,1156,1157],{"class":35,"line":566},[33,1158,1159],{"class":195},"    \u003C\u002F>\n",[33,1161,1163],{"class":35,"line":1162},27,[33,1164,336],{"class":195},[33,1166,1168],{"class":35,"line":1167},28,[33,1169,237],{"class":195},[14,1171,1173],{"id":1172},"compound-components-implicit-shared-state-via-context","Compound Components: Implicit Shared State via Context",[19,1175,1177],{"filename":1176,"language":22},"compound_components.js",[24,1178,1180],{"className":26,"code":1179,"language":22,"meta":28,"style":28},"\u002F\u002F A compound component is a GROUP of components designed to be used together,\n\u002F\u002F where a parent implicitly shares state with its children via Context — the\n\u002F\u002F children don't need props threaded explicitly, because the relationship is\n\u002F\u002F baked into how they're composed. Think \u003Cselect> + \u003Coption> in native HTML.\n\nimport { createContext, useContext, useState } from 'react'\n\nconst TabsContext = createContext(null)\n\nfunction Tabs({ defaultTab, children }) {\n  const [activeTab, setActiveTab] = useState(defaultTab)\n  return (\n    \u003CTabsContext.Provider value={{ activeTab, setActiveTab }}>\n      \u003Cdiv className=\"tabs\">{children}\u003C\u002Fdiv>\n    \u003C\u002FTabsContext.Provider>\n  )\n}\n\nfunction TabList({ children }) {\n  return \u003Cdiv className=\"tab-list\" role=\"tablist\">{children}\u003C\u002Fdiv>\n}\n\nfunction Tab({ id, children }) {\n  const { activeTab, setActiveTab } = useContext(TabsContext)\n  if (!activeTab) throw new Error('\u003CTabs.Tab> must be rendered inside \u003CTabs>')\n  return (\n    \u003Cbutton\n      role=\"tab\"\n      aria-selected={activeTab === id}\n      className={activeTab === id ? 'tab active' : 'tab'}\n      onClick={() => setActiveTab(id)}\n    >\n      {children}\n    \u003C\u002Fbutton>\n  )\n}\n\nfunction TabPanel({ id, children }) {\n  const { activeTab } = useContext(TabsContext)\n  if (!activeTab) throw new Error('\u003CTabs.Panel> must be rendered inside \u003CTabs>')\n  return activeTab === id ? \u003Cdiv role=\"tabpanel\">{children}\u003C\u002Fdiv> : null\n}\n\n\u002F\u002F Attach sub-components as static properties — purely NAMING\u002Fergonomics.\n\u002F\u002F The actual coupling is the shared Context, NOT the property assignment.\nTabs.List = TabList\nTabs.Tab = Tab\nTabs.Panel = TabPanel\n\n\u002F\u002F Consumer controls ARRANGEMENT — reordering, wrapping in conditionals,\n\u002F\u002F inserting markup between tabs and panels — all without Tabs needing an\n\u002F\u002F `items` prop shaped as a rigid array.\nfunction SettingsPage() {\n  return (\n    \u003CTabs defaultTab=\"profile\">\n      \u003CTabs.List>\n        \u003CTabs.Tab id=\"profile\">Profile\u003C\u002FTabs.Tab>\n        \u003CTabs.Tab id=\"billing\">Billing\u003C\u002FTabs.Tab>\n      \u003C\u002FTabs.List>\n      \u003CTabs.Panel id=\"profile\">\u003CProfileSettings \u002F>\u003C\u002FTabs.Panel>\n      \u003CTabs.Panel id=\"billing\">\u003CBillingSettings \u002F>\u003C\u002FTabs.Panel>\n    \u003C\u002FTabs>\n  )\n}\n",[30,1181,1182,1187,1192,1197,1202,1206,1220,1224,1245,1249,1267,1293,1299,1314,1333,1341,1345,1349,1353,1366,1395,1399,1403,1421,1445,1475,1481,1488,1498,1515,1544,1564,1570,1576,1585,1590,1595,1600,1618,1635,1659,1697,1702,1707,1713,1719,1730,1741,1752,1757,1763,1769,1775,1785,1792,1810,1820,1843,1864,1874,1901,1925,1934,1939],{"__ignoreMap":28},[33,1183,1184],{"class":35,"line":36},[33,1185,1186],{"class":39},"\u002F\u002F A compound component is a GROUP of components designed to be used together,\n",[33,1188,1189],{"class":35,"line":43},[33,1190,1191],{"class":39},"\u002F\u002F where a parent implicitly shares state with its children via Context — the\n",[33,1193,1194],{"class":35,"line":49},[33,1195,1196],{"class":39},"\u002F\u002F children don't need props threaded explicitly, because the relationship is\n",[33,1198,1199],{"class":35,"line":55},[33,1200,1201],{"class":39},"\u002F\u002F baked into how they're composed. Think \u003Cselect> + \u003Coption> in native HTML.\n",[33,1203,1204],{"class":35,"line":61},[33,1205,65],{"emptyLinePlaceholder":64},[33,1207,1208,1211,1214,1217],{"class":35,"line":68},[33,1209,1210],{"class":187},"import",[33,1212,1213],{"class":195}," { createContext, useContext, useState } ",[33,1215,1216],{"class":187},"from",[33,1218,1219],{"class":223}," 'react'\n",[33,1221,1222],{"class":35,"line":74},[33,1223,65],{"emptyLinePlaceholder":64},[33,1225,1226,1229,1232,1234,1237,1239,1242],{"class":35,"line":80},[33,1227,1228],{"class":187},"const",[33,1230,1231],{"class":290}," TabsContext",[33,1233,936],{"class":187},[33,1235,1236],{"class":191}," createContext",[33,1238,1057],{"class":195},[33,1240,1241],{"class":290},"null",[33,1243,1244],{"class":195},")\n",[33,1246,1247],{"class":35,"line":86},[33,1248,65],{"emptyLinePlaceholder":64},[33,1250,1251,1253,1256,1258,1261,1263,1265],{"class":35,"line":92},[33,1252,188],{"class":187},[33,1254,1255],{"class":191}," Tabs",[33,1257,196],{"class":195},[33,1259,1260],{"class":199},"defaultTab",[33,1262,387],{"class":195},[33,1264,154],{"class":199},[33,1266,202],{"class":195},[33,1268,1269,1271,1274,1277,1279,1282,1285,1287,1290],{"class":35,"line":97},[33,1270,968],{"class":187},[33,1272,1273],{"class":195}," [",[33,1275,1276],{"class":290},"activeTab",[33,1278,387],{"class":195},[33,1280,1281],{"class":290},"setActiveTab",[33,1283,1284],{"class":195},"] ",[33,1286,220],{"class":187},[33,1288,1289],{"class":191}," useState",[33,1291,1292],{"class":195},"(defaultTab)\n",[33,1294,1295,1297],{"class":35,"line":103},[33,1296,207],{"class":187},[33,1298,282],{"class":195},[33,1300,1301,1303,1306,1309,1311],{"class":35,"line":109},[33,1302,287],{"class":195},[33,1304,1305],{"class":290},"TabsContext.Provider",[33,1307,1308],{"class":191}," value",[33,1310,220],{"class":187},[33,1312,1313],{"class":195},"{{ activeTab, setActiveTab }}>\n",[33,1315,1316,1318,1320,1322,1324,1327,1329,1331],{"class":35,"line":115},[33,1317,298],{"class":195},[33,1319,214],{"class":213},[33,1321,217],{"class":191},[33,1323,220],{"class":187},[33,1325,1326],{"class":223},"\"tabs\"",[33,1328,227],{"class":195},[33,1330,214],{"class":213},[33,1332,232],{"class":195},[33,1334,1335,1337,1339],{"class":35,"line":121},[33,1336,326],{"class":195},[33,1338,1305],{"class":290},[33,1340,232],{"class":195},[33,1342,1343],{"class":35,"line":127},[33,1344,336],{"class":195},[33,1346,1347],{"class":35,"line":132},[33,1348,237],{"class":195},[33,1350,1351],{"class":35,"line":138},[33,1352,65],{"emptyLinePlaceholder":64},[33,1354,1355,1357,1360,1362,1364],{"class":35,"line":144},[33,1356,188],{"class":187},[33,1358,1359],{"class":191}," TabList",[33,1361,196],{"class":195},[33,1363,154],{"class":199},[33,1365,202],{"class":195},[33,1367,1368,1370,1372,1374,1376,1378,1381,1384,1386,1389,1391,1393],{"class":35,"line":333},[33,1369,207],{"class":187},[33,1371,210],{"class":195},[33,1373,214],{"class":213},[33,1375,217],{"class":191},[33,1377,220],{"class":187},[33,1379,1380],{"class":223},"\"tab-list\"",[33,1382,1383],{"class":191}," role",[33,1385,220],{"class":187},[33,1387,1388],{"class":223},"\"tablist\"",[33,1390,227],{"class":195},[33,1392,214],{"class":213},[33,1394,232],{"class":195},[33,1396,1397],{"class":35,"line":339},[33,1398,237],{"class":195},[33,1400,1401],{"class":35,"line":523},[33,1402,65],{"emptyLinePlaceholder":64},[33,1404,1405,1407,1410,1412,1415,1417,1419],{"class":35,"line":540},[33,1406,188],{"class":187},[33,1408,1409],{"class":191}," Tab",[33,1411,196],{"class":195},[33,1413,1414],{"class":199},"id",[33,1416,387],{"class":195},[33,1418,154],{"class":199},[33,1420,202],{"class":195},[33,1422,1423,1425,1428,1430,1432,1434,1437,1439,1442],{"class":35,"line":555},[33,1424,968],{"class":187},[33,1426,1427],{"class":195}," { ",[33,1429,1276],{"class":290},[33,1431,387],{"class":195},[33,1433,1281],{"class":290},[33,1435,1436],{"class":195}," } ",[33,1438,220],{"class":187},[33,1440,1441],{"class":191}," useContext",[33,1443,1444],{"class":195},"(TabsContext)\n",[33,1446,1447,1450,1453,1456,1459,1462,1465,1468,1470,1473],{"class":35,"line":561},[33,1448,1449],{"class":187},"  if",[33,1451,1452],{"class":195}," (",[33,1454,1455],{"class":187},"!",[33,1457,1458],{"class":195},"activeTab) ",[33,1460,1461],{"class":187},"throw",[33,1463,1464],{"class":187}," new",[33,1466,1467],{"class":191}," Error",[33,1469,1057],{"class":195},[33,1471,1472],{"class":223},"'\u003CTabs.Tab> must be rendered inside \u003CTabs>'",[33,1474,1244],{"class":195},[33,1476,1477,1479],{"class":35,"line":566},[33,1478,207],{"class":187},[33,1480,282],{"class":195},[33,1482,1483,1485],{"class":35,"line":1162},[33,1484,287],{"class":195},[33,1486,1487],{"class":213},"button\n",[33,1489,1490,1493,1495],{"class":35,"line":1167},[33,1491,1492],{"class":191},"      role",[33,1494,220],{"class":187},[33,1496,1497],{"class":223},"\"tab\"\n",[33,1499,1501,1504,1506,1509,1512],{"class":35,"line":1500},29,[33,1502,1503],{"class":191},"      aria-selected",[33,1505,220],{"class":187},[33,1507,1508],{"class":195},"{activeTab ",[33,1510,1511],{"class":187},"===",[33,1513,1514],{"class":195}," id}\n",[33,1516,1518,1521,1523,1525,1527,1530,1533,1536,1539,1542],{"class":35,"line":1517},30,[33,1519,1520],{"class":191},"      className",[33,1522,220],{"class":187},[33,1524,1508],{"class":195},[33,1526,1511],{"class":187},[33,1528,1529],{"class":195}," id ",[33,1531,1532],{"class":187},"?",[33,1534,1535],{"class":223}," 'tab active'",[33,1537,1538],{"class":187}," :",[33,1540,1541],{"class":223}," 'tab'",[33,1543,237],{"class":195},[33,1545,1547,1550,1552,1555,1558,1561],{"class":35,"line":1546},31,[33,1548,1549],{"class":191},"      onClick",[33,1551,220],{"class":187},[33,1553,1554],{"class":195},"{() ",[33,1556,1557],{"class":187},"=>",[33,1559,1560],{"class":191}," setActiveTab",[33,1562,1563],{"class":195},"(id)}\n",[33,1565,1567],{"class":35,"line":1566},32,[33,1568,1569],{"class":195},"    >\n",[33,1571,1573],{"class":35,"line":1572},33,[33,1574,1575],{"class":195},"      {children}\n",[33,1577,1579,1581,1583],{"class":35,"line":1578},34,[33,1580,326],{"class":195},[33,1582,994],{"class":213},[33,1584,232],{"class":195},[33,1586,1588],{"class":35,"line":1587},35,[33,1589,336],{"class":195},[33,1591,1593],{"class":35,"line":1592},36,[33,1594,237],{"class":195},[33,1596,1598],{"class":35,"line":1597},37,[33,1599,65],{"emptyLinePlaceholder":64},[33,1601,1603,1605,1608,1610,1612,1614,1616],{"class":35,"line":1602},38,[33,1604,188],{"class":187},[33,1606,1607],{"class":191}," TabPanel",[33,1609,196],{"class":195},[33,1611,1414],{"class":199},[33,1613,387],{"class":195},[33,1615,154],{"class":199},[33,1617,202],{"class":195},[33,1619,1621,1623,1625,1627,1629,1631,1633],{"class":35,"line":1620},39,[33,1622,968],{"class":187},[33,1624,1427],{"class":195},[33,1626,1276],{"class":290},[33,1628,1436],{"class":195},[33,1630,220],{"class":187},[33,1632,1441],{"class":191},[33,1634,1444],{"class":195},[33,1636,1638,1640,1642,1644,1646,1648,1650,1652,1654,1657],{"class":35,"line":1637},40,[33,1639,1449],{"class":187},[33,1641,1452],{"class":195},[33,1643,1455],{"class":187},[33,1645,1458],{"class":195},[33,1647,1461],{"class":187},[33,1649,1464],{"class":187},[33,1651,1467],{"class":191},[33,1653,1057],{"class":195},[33,1655,1656],{"class":223},"'\u003CTabs.Panel> must be rendered inside \u003CTabs>'",[33,1658,1244],{"class":195},[33,1660,1662,1664,1667,1669,1671,1673,1675,1677,1679,1681,1684,1686,1688,1691,1694],{"class":35,"line":1661},41,[33,1663,207],{"class":187},[33,1665,1666],{"class":195}," activeTab ",[33,1668,1511],{"class":187},[33,1670,1529],{"class":195},[33,1672,1532],{"class":187},[33,1674,210],{"class":195},[33,1676,214],{"class":213},[33,1678,1383],{"class":191},[33,1680,220],{"class":187},[33,1682,1683],{"class":223},"\"tabpanel\"",[33,1685,227],{"class":195},[33,1687,214],{"class":213},[33,1689,1690],{"class":195},"> ",[33,1692,1693],{"class":187},":",[33,1695,1696],{"class":290}," null\n",[33,1698,1700],{"class":35,"line":1699},42,[33,1701,237],{"class":195},[33,1703,1705],{"class":35,"line":1704},43,[33,1706,65],{"emptyLinePlaceholder":64},[33,1708,1710],{"class":35,"line":1709},44,[33,1711,1712],{"class":39},"\u002F\u002F Attach sub-components as static properties — purely NAMING\u002Fergonomics.\n",[33,1714,1716],{"class":35,"line":1715},45,[33,1717,1718],{"class":39},"\u002F\u002F The actual coupling is the shared Context, NOT the property assignment.\n",[33,1720,1722,1725,1727],{"class":35,"line":1721},46,[33,1723,1724],{"class":195},"Tabs.List ",[33,1726,220],{"class":187},[33,1728,1729],{"class":195}," TabList\n",[33,1731,1733,1736,1738],{"class":35,"line":1732},47,[33,1734,1735],{"class":195},"Tabs.Tab ",[33,1737,220],{"class":187},[33,1739,1740],{"class":195}," Tab\n",[33,1742,1744,1747,1749],{"class":35,"line":1743},48,[33,1745,1746],{"class":195},"Tabs.Panel ",[33,1748,220],{"class":187},[33,1750,1751],{"class":195}," TabPanel\n",[33,1753,1755],{"class":35,"line":1754},49,[33,1756,65],{"emptyLinePlaceholder":64},[33,1758,1760],{"class":35,"line":1759},50,[33,1761,1762],{"class":39},"\u002F\u002F Consumer controls ARRANGEMENT — reordering, wrapping in conditionals,\n",[33,1764,1766],{"class":35,"line":1765},51,[33,1767,1768],{"class":39},"\u002F\u002F inserting markup between tabs and panels — all without Tabs needing an\n",[33,1770,1772],{"class":35,"line":1771},52,[33,1773,1774],{"class":39},"\u002F\u002F `items` prop shaped as a rigid array.\n",[33,1776,1778,1780,1783],{"class":35,"line":1777},53,[33,1779,188],{"class":187},[33,1781,1782],{"class":191}," SettingsPage",[33,1784,275],{"class":195},[33,1786,1788,1790],{"class":35,"line":1787},54,[33,1789,207],{"class":187},[33,1791,282],{"class":195},[33,1793,1795,1797,1800,1803,1805,1808],{"class":35,"line":1794},55,[33,1796,287],{"class":195},[33,1798,1799],{"class":290},"Tabs",[33,1801,1802],{"class":191}," defaultTab",[33,1804,220],{"class":187},[33,1806,1807],{"class":223},"\"profile\"",[33,1809,232],{"class":195},[33,1811,1813,1815,1818],{"class":35,"line":1812},56,[33,1814,298],{"class":195},[33,1816,1817],{"class":290},"Tabs.List",[33,1819,232],{"class":195},[33,1821,1823,1826,1829,1832,1834,1836,1839,1841],{"class":35,"line":1822},57,[33,1824,1825],{"class":195},"        \u003C",[33,1827,1828],{"class":290},"Tabs.Tab",[33,1830,1831],{"class":191}," id",[33,1833,220],{"class":187},[33,1835,1807],{"class":223},[33,1837,1838],{"class":195},">Profile\u003C\u002F",[33,1840,1828],{"class":290},[33,1842,232],{"class":195},[33,1844,1846,1848,1850,1852,1854,1857,1860,1862],{"class":35,"line":1845},58,[33,1847,1825],{"class":195},[33,1849,1828],{"class":290},[33,1851,1831],{"class":191},[33,1853,220],{"class":187},[33,1855,1856],{"class":223},"\"billing\"",[33,1858,1859],{"class":195},">Billing\u003C\u002F",[33,1861,1828],{"class":290},[33,1863,232],{"class":195},[33,1865,1867,1870,1872],{"class":35,"line":1866},59,[33,1868,1869],{"class":195},"      \u003C\u002F",[33,1871,1817],{"class":290},[33,1873,232],{"class":195},[33,1875,1877,1879,1882,1884,1886,1888,1891,1894,1897,1899],{"class":35,"line":1876},60,[33,1878,298],{"class":195},[33,1880,1881],{"class":290},"Tabs.Panel",[33,1883,1831],{"class":191},[33,1885,220],{"class":187},[33,1887,1807],{"class":223},[33,1889,1890],{"class":195},">\u003C",[33,1892,1893],{"class":290},"ProfileSettings",[33,1895,1896],{"class":195}," \u002F>\u003C\u002F",[33,1898,1881],{"class":290},[33,1900,232],{"class":195},[33,1902,1904,1906,1908,1910,1912,1914,1916,1919,1921,1923],{"class":35,"line":1903},61,[33,1905,298],{"class":195},[33,1907,1881],{"class":290},[33,1909,1831],{"class":191},[33,1911,220],{"class":187},[33,1913,1856],{"class":223},[33,1915,1890],{"class":195},[33,1917,1918],{"class":290},"BillingSettings",[33,1920,1896],{"class":195},[33,1922,1881],{"class":290},[33,1924,232],{"class":195},[33,1926,1928,1930,1932],{"class":35,"line":1927},62,[33,1929,326],{"class":195},[33,1931,1799],{"class":290},[33,1933,232],{"class":195},[33,1935,1937],{"class":35,"line":1936},63,[33,1938,336],{"class":195},[33,1940,1942],{"class":35,"line":1941},64,[33,1943,237],{"class":195},[570,1945,1947],{"id":1946},"the-rigid-alternative-and-when-its-fine","The Rigid Alternative (and When It's Fine)",[19,1949,1951],{"filename":1950,"language":22},"rigid_tabs.js",[24,1952,1954],{"className":26,"code":1953,"language":22,"meta":28,"style":28},"\u002F\u002F The rigid, data-driven alternative — works, but the caller loses ALL control\n\u002F\u002F over arrangement and interleaved markup. Choose this when flexibility does NOT\n\u002F\u002F matter (fixed structure, no rearranging needed); choose compound when it does.\n\nfunction RigidTabs({ items, defaultTab }) {\n  const [activeTab, setActiveTab] = useState(defaultTab)\n  return (\n    \u003Cdiv>\n      \u003Cdiv className=\"tab-list\">\n        {items.map(item => (\n          \u003Cbutton key={item.id} onClick={() => setActiveTab(item.id)}>{item.label}\u003C\u002Fbutton>\n        ))}\n      \u003C\u002Fdiv>\n      {items.find(item => item.id === activeTab)?.content}\n    \u003C\u002Fdiv>\n  )\n}\n\n\u002F\u002F Usage — simple, but you can't insert arbitrary markup between tabs, wrap one\n\u002F\u002F panel in a conditional, or reorder without changing the items array.\n;\u003CRigidTabs\n  items={[\n    { id: 'profile', label: 'Profile', content: \u003CProfileSettings \u002F> },\n    { id: 'billing', label: 'Billing', content: \u003CBillingSettings \u002F> },\n  ]}\n  defaultTab=\"profile\"\n\u002F>\n",[30,1955,1956,1961,1966,1971,1975,1993,2013,2019,2027,2041,2059,2092,2097,2105,2127,2135,2139,2143,2147,2152,2157,2166,2176,2198,2216,2221,2226],{"__ignoreMap":28},[33,1957,1958],{"class":35,"line":36},[33,1959,1960],{"class":39},"\u002F\u002F The rigid, data-driven alternative — works, but the caller loses ALL control\n",[33,1962,1963],{"class":35,"line":43},[33,1964,1965],{"class":39},"\u002F\u002F over arrangement and interleaved markup. Choose this when flexibility does NOT\n",[33,1967,1968],{"class":35,"line":49},[33,1969,1970],{"class":39},"\u002F\u002F matter (fixed structure, no rearranging needed); choose compound when it does.\n",[33,1972,1973],{"class":35,"line":55},[33,1974,65],{"emptyLinePlaceholder":64},[33,1976,1977,1979,1982,1984,1987,1989,1991],{"class":35,"line":61},[33,1978,188],{"class":187},[33,1980,1981],{"class":191}," RigidTabs",[33,1983,196],{"class":195},[33,1985,1986],{"class":199},"items",[33,1988,387],{"class":195},[33,1990,1260],{"class":199},[33,1992,202],{"class":195},[33,1994,1995,1997,1999,2001,2003,2005,2007,2009,2011],{"class":35,"line":68},[33,1996,968],{"class":187},[33,1998,1273],{"class":195},[33,2000,1276],{"class":290},[33,2002,387],{"class":195},[33,2004,1281],{"class":290},[33,2006,1284],{"class":195},[33,2008,220],{"class":187},[33,2010,1289],{"class":191},[33,2012,1292],{"class":195},[33,2014,2015,2017],{"class":35,"line":74},[33,2016,207],{"class":187},[33,2018,282],{"class":195},[33,2020,2021,2023,2025],{"class":35,"line":80},[33,2022,287],{"class":195},[33,2024,214],{"class":213},[33,2026,232],{"class":195},[33,2028,2029,2031,2033,2035,2037,2039],{"class":35,"line":86},[33,2030,298],{"class":195},[33,2032,214],{"class":213},[33,2034,217],{"class":191},[33,2036,220],{"class":187},[33,2038,1380],{"class":223},[33,2040,232],{"class":195},[33,2042,2043,2046,2049,2051,2054,2057],{"class":35,"line":92},[33,2044,2045],{"class":195},"        {items.",[33,2047,2048],{"class":191},"map",[33,2050,1057],{"class":195},[33,2052,2053],{"class":199},"item",[33,2055,2056],{"class":187}," =>",[33,2058,282],{"class":195},[33,2060,2061,2064,2066,2069,2071,2074,2077,2079,2081,2083,2085,2088,2090],{"class":35,"line":97},[33,2062,2063],{"class":195},"          \u003C",[33,2065,994],{"class":213},[33,2067,2068],{"class":191}," key",[33,2070,220],{"class":187},[33,2072,2073],{"class":195},"{item.id} ",[33,2075,2076],{"class":191},"onClick",[33,2078,220],{"class":187},[33,2080,1554],{"class":195},[33,2082,1557],{"class":187},[33,2084,1560],{"class":191},[33,2086,2087],{"class":195},"(item.id)}>{item.label}\u003C\u002F",[33,2089,994],{"class":213},[33,2091,232],{"class":195},[33,2093,2094],{"class":35,"line":103},[33,2095,2096],{"class":195},"        ))}\n",[33,2098,2099,2101,2103],{"class":35,"line":109},[33,2100,1869],{"class":195},[33,2102,214],{"class":213},[33,2104,232],{"class":195},[33,2106,2107,2110,2113,2115,2117,2119,2122,2124],{"class":35,"line":115},[33,2108,2109],{"class":195},"      {items.",[33,2111,2112],{"class":191},"find",[33,2114,1057],{"class":195},[33,2116,2053],{"class":199},[33,2118,2056],{"class":187},[33,2120,2121],{"class":195}," item.id ",[33,2123,1511],{"class":187},[33,2125,2126],{"class":195}," activeTab)?.content}\n",[33,2128,2129,2131,2133],{"class":35,"line":121},[33,2130,326],{"class":195},[33,2132,214],{"class":213},[33,2134,232],{"class":195},[33,2136,2137],{"class":35,"line":127},[33,2138,336],{"class":195},[33,2140,2141],{"class":35,"line":132},[33,2142,237],{"class":195},[33,2144,2145],{"class":35,"line":138},[33,2146,65],{"emptyLinePlaceholder":64},[33,2148,2149],{"class":35,"line":144},[33,2150,2151],{"class":39},"\u002F\u002F Usage — simple, but you can't insert arbitrary markup between tabs, wrap one\n",[33,2153,2154],{"class":35,"line":333},[33,2155,2156],{"class":39},"\u002F\u002F panel in a conditional, or reorder without changing the items array.\n",[33,2158,2159,2161,2163],{"class":35,"line":339},[33,2160,780],{"class":195},[33,2162,783],{"class":187},[33,2164,2165],{"class":195},"RigidTabs\n",[33,2167,2168,2171,2173],{"class":35,"line":523},[33,2169,2170],{"class":195},"  items",[33,2172,220],{"class":187},[33,2174,2175],{"class":195},"{[\n",[33,2177,2178,2181,2184,2187,2190,2193,2195],{"class":35,"line":540},[33,2179,2180],{"class":195},"    { id: ",[33,2182,2183],{"class":223},"'profile'",[33,2185,2186],{"class":195},", label: ",[33,2188,2189],{"class":223},"'Profile'",[33,2191,2192],{"class":195},", content: \u003C",[33,2194,1893],{"class":290},[33,2196,2197],{"class":195}," \u002F> },\n",[33,2199,2200,2202,2205,2207,2210,2212,2214],{"class":35,"line":555},[33,2201,2180],{"class":195},[33,2203,2204],{"class":223},"'billing'",[33,2206,2186],{"class":195},[33,2208,2209],{"class":223},"'Billing'",[33,2211,2192],{"class":195},[33,2213,1918],{"class":290},[33,2215,2197],{"class":195},[33,2217,2218],{"class":35,"line":561},[33,2219,2220],{"class":195},"  ]}\n",[33,2222,2223],{"class":35,"line":566},[33,2224,2225],{"class":195},"  defaultTab=\"profile\"\n",[33,2227,2228],{"class":35,"line":1162},[33,2229,885],{"class":195},[570,2231,2233,2234],{"id":2232},"compound-component-with-dynamic-children-via-reactchildren","Compound Component with Dynamic Children via ",[30,2235,2236],{},"React.Children",[19,2238,2240],{"filename":2239,"language":22},"compound_dynamic_children.js",[24,2241,2243],{"className":26,"code":2242,"language":22,"meta":28,"style":28},"\u002F\u002F Advanced: the parent can inspect and clone its children to inject implicit\n\u002F\u002F props (like the active tab id) without the child explicitly reading Context.\n\u002F\u002F This is how libraries like Reach UI and Radix UI build compound components.\n\nimport { Children, cloneElement, isValidElement } from 'react'\n\nfunction Select({ value, onChange, children }) {\n  return (\n    \u003Cdiv className=\"select\">\n      {Children.map(children, child => {\n        \u002F\u002F Inject `isActive` and `onSelect` into each \u003COption> without the caller\n        \u002F\u002F threading those props manually. cloneElement merges the injected props.\n        if (isValidElement(child)) {\n          return cloneElement(child, {\n            isActive: child.props.value === value,\n            onSelect: () => onChange(child.props.value),\n          })\n        }\n        return child\n      })}\n    \u003C\u002Fdiv>\n  )\n}\n\nfunction Option({ value, isActive, onSelect, children }) {\n  return (\n    \u003Cbutton className={isActive ? 'option active' : 'option'} onClick={onSelect}>\n      {children}\n    \u003C\u002Fbutton>\n  )\n}\n\nSelect.Option = Option\n\n\u002F\u002F Consumer doesn't pass isActive\u002FonSelect — the parent injects them:\n;\u003CSelect value=\"apple\" onChange={setFruit}>\n  \u003CSelect.Option value=\"apple\">Apple\u003C\u002FSelect.Option>\n  \u003CSelect.Option value=\"banana\">Banana\u003C\u002FSelect.Option>\n\u003C\u002FSelect>\n",[30,2244,2245,2250,2255,2260,2264,2275,2279,2302,2308,2323,2341,2346,2351,2364,2375,2385,2401,2406,2411,2419,2424,2432,2436,2440,2444,2471,2477,2510,2514,2522,2526,2530,2534,2544,2548,2553,2576,2597,2617],{"__ignoreMap":28},[33,2246,2247],{"class":35,"line":36},[33,2248,2249],{"class":39},"\u002F\u002F Advanced: the parent can inspect and clone its children to inject implicit\n",[33,2251,2252],{"class":35,"line":43},[33,2253,2254],{"class":39},"\u002F\u002F props (like the active tab id) without the child explicitly reading Context.\n",[33,2256,2257],{"class":35,"line":49},[33,2258,2259],{"class":39},"\u002F\u002F This is how libraries like Reach UI and Radix UI build compound components.\n",[33,2261,2262],{"class":35,"line":55},[33,2263,65],{"emptyLinePlaceholder":64},[33,2265,2266,2268,2271,2273],{"class":35,"line":61},[33,2267,1210],{"class":187},[33,2269,2270],{"class":195}," { Children, cloneElement, isValidElement } ",[33,2272,1216],{"class":187},[33,2274,1219],{"class":223},[33,2276,2277],{"class":35,"line":68},[33,2278,65],{"emptyLinePlaceholder":64},[33,2280,2281,2283,2286,2288,2291,2293,2296,2298,2300],{"class":35,"line":74},[33,2282,188],{"class":187},[33,2284,2285],{"class":191}," Select",[33,2287,196],{"class":195},[33,2289,2290],{"class":199},"value",[33,2292,387],{"class":195},[33,2294,2295],{"class":199},"onChange",[33,2297,387],{"class":195},[33,2299,154],{"class":199},[33,2301,202],{"class":195},[33,2303,2304,2306],{"class":35,"line":80},[33,2305,207],{"class":187},[33,2307,282],{"class":195},[33,2309,2310,2312,2314,2316,2318,2321],{"class":35,"line":86},[33,2311,287],{"class":195},[33,2313,214],{"class":213},[33,2315,217],{"class":191},[33,2317,220],{"class":187},[33,2319,2320],{"class":223},"\"select\"",[33,2322,232],{"class":195},[33,2324,2325,2328,2330,2333,2336,2338],{"class":35,"line":92},[33,2326,2327],{"class":195},"      {Children.",[33,2329,2048],{"class":191},[33,2331,2332],{"class":195},"(children, ",[33,2334,2335],{"class":199},"child",[33,2337,2056],{"class":187},[33,2339,2340],{"class":195}," {\n",[33,2342,2343],{"class":35,"line":97},[33,2344,2345],{"class":39},"        \u002F\u002F Inject `isActive` and `onSelect` into each \u003COption> without the caller\n",[33,2347,2348],{"class":35,"line":103},[33,2349,2350],{"class":39},"        \u002F\u002F threading those props manually. cloneElement merges the injected props.\n",[33,2352,2353,2356,2358,2361],{"class":35,"line":109},[33,2354,2355],{"class":187},"        if",[33,2357,1452],{"class":195},[33,2359,2360],{"class":191},"isValidElement",[33,2362,2363],{"class":195},"(child)) {\n",[33,2365,2366,2369,2372],{"class":35,"line":115},[33,2367,2368],{"class":187},"          return",[33,2370,2371],{"class":191}," cloneElement",[33,2373,2374],{"class":195},"(child, {\n",[33,2376,2377,2380,2382],{"class":35,"line":121},[33,2378,2379],{"class":195},"            isActive: child.props.value ",[33,2381,1511],{"class":187},[33,2383,2384],{"class":195}," value,\n",[33,2386,2387,2390,2393,2395,2398],{"class":35,"line":127},[33,2388,2389],{"class":191},"            onSelect",[33,2391,2392],{"class":195},": () ",[33,2394,1557],{"class":187},[33,2396,2397],{"class":191}," onChange",[33,2399,2400],{"class":195},"(child.props.value),\n",[33,2402,2403],{"class":35,"line":132},[33,2404,2405],{"class":195},"          })\n",[33,2407,2408],{"class":35,"line":138},[33,2409,2410],{"class":195},"        }\n",[33,2412,2413,2416],{"class":35,"line":144},[33,2414,2415],{"class":187},"        return",[33,2417,2418],{"class":195}," child\n",[33,2420,2421],{"class":35,"line":333},[33,2422,2423],{"class":195},"      })}\n",[33,2425,2426,2428,2430],{"class":35,"line":339},[33,2427,326],{"class":195},[33,2429,214],{"class":213},[33,2431,232],{"class":195},[33,2433,2434],{"class":35,"line":523},[33,2435,336],{"class":195},[33,2437,2438],{"class":35,"line":540},[33,2439,237],{"class":195},[33,2441,2442],{"class":35,"line":555},[33,2443,65],{"emptyLinePlaceholder":64},[33,2445,2446,2448,2451,2453,2455,2457,2460,2462,2465,2467,2469],{"class":35,"line":561},[33,2447,188],{"class":187},[33,2449,2450],{"class":191}," Option",[33,2452,196],{"class":195},[33,2454,2290],{"class":199},[33,2456,387],{"class":195},[33,2458,2459],{"class":199},"isActive",[33,2461,387],{"class":195},[33,2463,2464],{"class":199},"onSelect",[33,2466,387],{"class":195},[33,2468,154],{"class":199},[33,2470,202],{"class":195},[33,2472,2473,2475],{"class":35,"line":566},[33,2474,207],{"class":187},[33,2476,282],{"class":195},[33,2478,2479,2481,2483,2485,2487,2490,2492,2495,2497,2500,2503,2505,2507],{"class":35,"line":1162},[33,2480,287],{"class":195},[33,2482,994],{"class":213},[33,2484,217],{"class":191},[33,2486,220],{"class":187},[33,2488,2489],{"class":195},"{isActive ",[33,2491,1532],{"class":187},[33,2493,2494],{"class":223}," 'option active'",[33,2496,1538],{"class":187},[33,2498,2499],{"class":223}," 'option'",[33,2501,2502],{"class":195},"} ",[33,2504,2076],{"class":191},[33,2506,220],{"class":187},[33,2508,2509],{"class":195},"{onSelect}>\n",[33,2511,2512],{"class":35,"line":1167},[33,2513,1575],{"class":195},[33,2515,2516,2518,2520],{"class":35,"line":1500},[33,2517,326],{"class":195},[33,2519,994],{"class":213},[33,2521,232],{"class":195},[33,2523,2524],{"class":35,"line":1517},[33,2525,336],{"class":195},[33,2527,2528],{"class":35,"line":1546},[33,2529,237],{"class":195},[33,2531,2532],{"class":35,"line":1566},[33,2533,65],{"emptyLinePlaceholder":64},[33,2535,2536,2539,2541],{"class":35,"line":1572},[33,2537,2538],{"class":195},"Select.Option ",[33,2540,220],{"class":187},[33,2542,2543],{"class":195}," Option\n",[33,2545,2546],{"class":35,"line":1578},[33,2547,65],{"emptyLinePlaceholder":64},[33,2549,2550],{"class":35,"line":1587},[33,2551,2552],{"class":39},"\u002F\u002F Consumer doesn't pass isActive\u002FonSelect — the parent injects them:\n",[33,2554,2555,2557,2559,2562,2564,2567,2569,2571,2574],{"class":35,"line":1592},[33,2556,780],{"class":195},[33,2558,783],{"class":187},[33,2560,2561],{"class":195},"Select value",[33,2563,220],{"class":187},[33,2565,2566],{"class":223},"\"apple\"",[33,2568,2397],{"class":195},[33,2570,220],{"class":187},[33,2572,2573],{"class":195},"{setFruit}",[33,2575,232],{"class":187},[33,2577,2578,2581,2584,2586,2588,2590,2593,2595],{"class":35,"line":1597},[33,2579,2580],{"class":195},"  \u003C",[33,2582,2583],{"class":290},"Select.Option",[33,2585,1308],{"class":191},[33,2587,220],{"class":187},[33,2589,2566],{"class":223},[33,2591,2592],{"class":195},">Apple\u003C\u002F",[33,2594,2583],{"class":290},[33,2596,232],{"class":195},[33,2598,2599,2601,2603,2605,2607,2610,2613,2615],{"class":35,"line":1602},[33,2600,2580],{"class":195},[33,2602,2583],{"class":290},[33,2604,1308],{"class":191},[33,2606,220],{"class":187},[33,2608,2609],{"class":223},"\"banana\"",[33,2611,2612],{"class":195},">Banana\u003C\u002F",[33,2614,2583],{"class":290},[33,2616,232],{"class":195},[33,2618,2619,2622,2625],{"class":35,"line":1620},[33,2620,2621],{"class":187},"\u003C\u002F",[33,2623,2624],{"class":195},"Select",[33,2626,232],{"class":187},[14,2628,2630],{"id":2629},"render-props-inverting-control-via-a-function-prop","Render Props: Inverting Control via a Function Prop",[19,2632,2634],{"filename":2633,"language":22},"render_props.js",[24,2635,2637],{"className":26,"code":2636,"language":22,"meta":28,"style":28},"\u002F\u002F A render prop is a function prop a component calls to determine what to render,\n\u002F\u002F giving the CALLER control over the rendered output while the COMPONENT owns\n\u002F\u002F the state. Before hooks (chapter 11), this was the primary pattern for sharing\n\u002F\u002F stateful rendering logic; it's still useful when the shared logic is inherently\n\u002F\u002F about WHAT to render, not just state management.\n\nfunction MouseTracker({ render }) {\n  const [position, setPosition] = useState({ x: 0, y: 0 })\n\n  useEffect(() => {\n    const handleMove = (e) => setPosition({ x: e.clientX, y: e.clientY })\n    window.addEventListener('mousemove', handleMove)\n    return () => window.removeEventListener('mousemove', handleMove)\n  }, [])\n\n  \u002F\u002F The component owns the mouse position state; the CALLER decides what to do\n  \u002F\u002F with it by passing a render function that receives the position.\n  return render(position)\n}\n\n\u002F\u002F Caller has full control over the rendered output:\nfunction App() {\n  return (\n    \u003CMouseTracker render={({ x, y }) => (\n      \u003Cp>The mouse is at {x}, {y}\u003C\u002Fp>\n    )} \u002F>\n  )\n}\n\n\u002F\u002F A more practical use: reusable data-fetching render prop (pre-React Query era)\nfunction DataFetcher({ url, render }) {\n  const [state, setState] = useState({ status: 'loading', data: null, error: null })\n\n  useEffect(() => {\n    let cancelled = false\n    fetch(url)\n      .then(res => res.json())\n      .then(data => { if (!cancelled) setState({ status: 'success', data, error: null }) })\n      .catch(error => { if (!cancelled) setState({ status: 'error', data: null, error }) })\n    return () => { cancelled = true }\n  }, [url])\n\n  return render(state)\n}\n\n\u002F\u002F Usage — caller controls rendering for each state, component owns the fetch logic\nfunction UserList() {\n  return (\n    \u003CDataFetcher\n      url=\"\u002Fapi\u002Fusers\"\n      render={({ status, data, error }) => {\n        if (status === 'loading') return \u003CSpinner \u002F>\n        if (status === 'error') return \u003CErrorMessage error={error} \u002F>\n        return \u003Cul>{data.map(u => \u003Cli key={u.id}>{u.name}\u003C\u002Fli>)}\u003C\u002Ful>\n      }}\n    \u002F>\n  )\n}\n",[30,2638,2639,2644,2649,2654,2659,2664,2668,2682,2716,2720,2732,2758,2774,2796,2801,2805,2810,2815,2825,2829,2833,2838,2846,2852,2881,2894,2899,2903,2907,2911,2916,2934,2972,2976,2986,2999,3007,3031,3071,3109,3128,3133,3137,3146,3150,3154,3159,3168,3174,3181,3191,3217,3242,3270,3312,3317,3321,3325],{"__ignoreMap":28},[33,2640,2641],{"class":35,"line":36},[33,2642,2643],{"class":39},"\u002F\u002F A render prop is a function prop a component calls to determine what to render,\n",[33,2645,2646],{"class":35,"line":43},[33,2647,2648],{"class":39},"\u002F\u002F giving the CALLER control over the rendered output while the COMPONENT owns\n",[33,2650,2651],{"class":35,"line":49},[33,2652,2653],{"class":39},"\u002F\u002F the state. Before hooks (chapter 11), this was the primary pattern for sharing\n",[33,2655,2656],{"class":35,"line":55},[33,2657,2658],{"class":39},"\u002F\u002F stateful rendering logic; it's still useful when the shared logic is inherently\n",[33,2660,2661],{"class":35,"line":61},[33,2662,2663],{"class":39},"\u002F\u002F about WHAT to render, not just state management.\n",[33,2665,2666],{"class":35,"line":68},[33,2667,65],{"emptyLinePlaceholder":64},[33,2669,2670,2672,2675,2677,2680],{"class":35,"line":74},[33,2671,188],{"class":187},[33,2673,2674],{"class":191}," MouseTracker",[33,2676,196],{"class":195},[33,2678,2679],{"class":199},"render",[33,2681,202],{"class":195},[33,2683,2684,2686,2688,2691,2693,2696,2698,2700,2702,2705,2708,2711,2713],{"class":35,"line":80},[33,2685,968],{"class":187},[33,2687,1273],{"class":195},[33,2689,2690],{"class":290},"position",[33,2692,387],{"class":195},[33,2694,2695],{"class":290},"setPosition",[33,2697,1284],{"class":195},[33,2699,220],{"class":187},[33,2701,1289],{"class":191},[33,2703,2704],{"class":195},"({ x: ",[33,2706,2707],{"class":290},"0",[33,2709,2710],{"class":195},", y: ",[33,2712,2707],{"class":290},[33,2714,2715],{"class":195}," })\n",[33,2717,2718],{"class":35,"line":86},[33,2719,65],{"emptyLinePlaceholder":64},[33,2721,2722,2725,2728,2730],{"class":35,"line":92},[33,2723,2724],{"class":191},"  useEffect",[33,2726,2727],{"class":195},"(() ",[33,2729,1557],{"class":187},[33,2731,2340],{"class":195},[33,2733,2734,2737,2740,2742,2744,2747,2750,2752,2755],{"class":35,"line":97},[33,2735,2736],{"class":187},"    const",[33,2738,2739],{"class":191}," handleMove",[33,2741,936],{"class":187},[33,2743,1452],{"class":195},[33,2745,2746],{"class":199},"e",[33,2748,2749],{"class":195},") ",[33,2751,1557],{"class":187},[33,2753,2754],{"class":191}," setPosition",[33,2756,2757],{"class":195},"({ x: e.clientX, y: e.clientY })\n",[33,2759,2760,2763,2766,2768,2771],{"class":35,"line":103},[33,2761,2762],{"class":195},"    window.",[33,2764,2765],{"class":191},"addEventListener",[33,2767,1057],{"class":195},[33,2769,2770],{"class":223},"'mousemove'",[33,2772,2773],{"class":195},", handleMove)\n",[33,2775,2776,2779,2782,2784,2787,2790,2792,2794],{"class":35,"line":109},[33,2777,2778],{"class":187},"    return",[33,2780,2781],{"class":195}," () ",[33,2783,1557],{"class":187},[33,2785,2786],{"class":195}," window.",[33,2788,2789],{"class":191},"removeEventListener",[33,2791,1057],{"class":195},[33,2793,2770],{"class":223},[33,2795,2773],{"class":195},[33,2797,2798],{"class":35,"line":115},[33,2799,2800],{"class":195},"  }, [])\n",[33,2802,2803],{"class":35,"line":121},[33,2804,65],{"emptyLinePlaceholder":64},[33,2806,2807],{"class":35,"line":127},[33,2808,2809],{"class":39},"  \u002F\u002F The component owns the mouse position state; the CALLER decides what to do\n",[33,2811,2812],{"class":35,"line":132},[33,2813,2814],{"class":39},"  \u002F\u002F with it by passing a render function that receives the position.\n",[33,2816,2817,2819,2822],{"class":35,"line":138},[33,2818,207],{"class":187},[33,2820,2821],{"class":191}," render",[33,2823,2824],{"class":195},"(position)\n",[33,2826,2827],{"class":35,"line":144},[33,2828,237],{"class":195},[33,2830,2831],{"class":35,"line":333},[33,2832,65],{"emptyLinePlaceholder":64},[33,2834,2835],{"class":35,"line":339},[33,2836,2837],{"class":39},"\u002F\u002F Caller has full control over the rendered output:\n",[33,2839,2840,2842,2844],{"class":35,"line":523},[33,2841,188],{"class":187},[33,2843,505],{"class":191},[33,2845,275],{"class":195},[33,2847,2848,2850],{"class":35,"line":540},[33,2849,207],{"class":187},[33,2851,282],{"class":195},[33,2853,2854,2856,2859,2861,2863,2866,2869,2871,2874,2877,2879],{"class":35,"line":555},[33,2855,287],{"class":195},[33,2857,2858],{"class":290},"MouseTracker",[33,2860,2821],{"class":191},[33,2862,220],{"class":187},[33,2864,2865],{"class":195},"{({ ",[33,2867,2868],{"class":199},"x",[33,2870,387],{"class":195},[33,2872,2873],{"class":199},"y",[33,2875,2876],{"class":195}," }) ",[33,2878,1557],{"class":187},[33,2880,282],{"class":195},[33,2882,2883,2885,2887,2890,2892],{"class":35,"line":561},[33,2884,298],{"class":195},[33,2886,314],{"class":213},[33,2888,2889],{"class":195},">The mouse is at {x}, {y}\u003C\u002F",[33,2891,314],{"class":213},[33,2893,232],{"class":195},[33,2895,2896],{"class":35,"line":566},[33,2897,2898],{"class":195},"    )} \u002F>\n",[33,2900,2901],{"class":35,"line":1162},[33,2902,336],{"class":195},[33,2904,2905],{"class":35,"line":1167},[33,2906,237],{"class":195},[33,2908,2909],{"class":35,"line":1500},[33,2910,65],{"emptyLinePlaceholder":64},[33,2912,2913],{"class":35,"line":1517},[33,2914,2915],{"class":39},"\u002F\u002F A more practical use: reusable data-fetching render prop (pre-React Query era)\n",[33,2917,2918,2920,2923,2925,2928,2930,2932],{"class":35,"line":1546},[33,2919,188],{"class":187},[33,2921,2922],{"class":191}," DataFetcher",[33,2924,196],{"class":195},[33,2926,2927],{"class":199},"url",[33,2929,387],{"class":195},[33,2931,2679],{"class":199},[33,2933,202],{"class":195},[33,2935,2936,2938,2940,2943,2945,2948,2950,2952,2954,2957,2960,2963,2965,2968,2970],{"class":35,"line":1566},[33,2937,968],{"class":187},[33,2939,1273],{"class":195},[33,2941,2942],{"class":290},"state",[33,2944,387],{"class":195},[33,2946,2947],{"class":290},"setState",[33,2949,1284],{"class":195},[33,2951,220],{"class":187},[33,2953,1289],{"class":191},[33,2955,2956],{"class":195},"({ status: ",[33,2958,2959],{"class":223},"'loading'",[33,2961,2962],{"class":195},", data: ",[33,2964,1241],{"class":290},[33,2966,2967],{"class":195},", error: ",[33,2969,1241],{"class":290},[33,2971,2715],{"class":195},[33,2973,2974],{"class":35,"line":1572},[33,2975,65],{"emptyLinePlaceholder":64},[33,2977,2978,2980,2982,2984],{"class":35,"line":1578},[33,2979,2724],{"class":191},[33,2981,2727],{"class":195},[33,2983,1557],{"class":187},[33,2985,2340],{"class":195},[33,2987,2988,2991,2994,2996],{"class":35,"line":1587},[33,2989,2990],{"class":187},"    let",[33,2992,2993],{"class":195}," cancelled ",[33,2995,220],{"class":187},[33,2997,2998],{"class":290}," false\n",[33,3000,3001,3004],{"class":35,"line":1592},[33,3002,3003],{"class":191},"    fetch",[33,3005,3006],{"class":195},"(url)\n",[33,3008,3009,3012,3015,3017,3020,3022,3025,3028],{"class":35,"line":1597},[33,3010,3011],{"class":195},"      .",[33,3013,3014],{"class":191},"then",[33,3016,1057],{"class":195},[33,3018,3019],{"class":199},"res",[33,3021,2056],{"class":187},[33,3023,3024],{"class":195}," res.",[33,3026,3027],{"class":191},"json",[33,3029,3030],{"class":195},"())\n",[33,3032,3033,3035,3037,3039,3042,3044,3046,3049,3051,3053,3056,3058,3060,3063,3066,3068],{"class":35,"line":1602},[33,3034,3011],{"class":195},[33,3036,3014],{"class":191},[33,3038,1057],{"class":195},[33,3040,3041],{"class":199},"data",[33,3043,2056],{"class":187},[33,3045,1427],{"class":195},[33,3047,3048],{"class":187},"if",[33,3050,1452],{"class":195},[33,3052,1455],{"class":187},[33,3054,3055],{"class":195},"cancelled) ",[33,3057,2947],{"class":191},[33,3059,2956],{"class":195},[33,3061,3062],{"class":223},"'success'",[33,3064,3065],{"class":195},", data, error: ",[33,3067,1241],{"class":290},[33,3069,3070],{"class":195}," }) })\n",[33,3072,3073,3075,3078,3080,3083,3085,3087,3089,3091,3093,3095,3097,3099,3102,3104,3106],{"class":35,"line":1620},[33,3074,3011],{"class":195},[33,3076,3077],{"class":191},"catch",[33,3079,1057],{"class":195},[33,3081,3082],{"class":199},"error",[33,3084,2056],{"class":187},[33,3086,1427],{"class":195},[33,3088,3048],{"class":187},[33,3090,1452],{"class":195},[33,3092,1455],{"class":187},[33,3094,3055],{"class":195},[33,3096,2947],{"class":191},[33,3098,2956],{"class":195},[33,3100,3101],{"class":223},"'error'",[33,3103,2962],{"class":195},[33,3105,1241],{"class":290},[33,3107,3108],{"class":195},", error }) })\n",[33,3110,3111,3113,3115,3117,3120,3122,3125],{"class":35,"line":1637},[33,3112,2778],{"class":187},[33,3114,2781],{"class":195},[33,3116,1557],{"class":187},[33,3118,3119],{"class":195}," { cancelled ",[33,3121,220],{"class":187},[33,3123,3124],{"class":290}," true",[33,3126,3127],{"class":195}," }\n",[33,3129,3130],{"class":35,"line":1661},[33,3131,3132],{"class":195},"  }, [url])\n",[33,3134,3135],{"class":35,"line":1699},[33,3136,65],{"emptyLinePlaceholder":64},[33,3138,3139,3141,3143],{"class":35,"line":1704},[33,3140,207],{"class":187},[33,3142,2821],{"class":191},[33,3144,3145],{"class":195},"(state)\n",[33,3147,3148],{"class":35,"line":1709},[33,3149,237],{"class":195},[33,3151,3152],{"class":35,"line":1715},[33,3153,65],{"emptyLinePlaceholder":64},[33,3155,3156],{"class":35,"line":1721},[33,3157,3158],{"class":39},"\u002F\u002F Usage — caller controls rendering for each state, component owns the fetch logic\n",[33,3160,3161,3163,3166],{"class":35,"line":1732},[33,3162,188],{"class":187},[33,3164,3165],{"class":191}," UserList",[33,3167,275],{"class":195},[33,3169,3170,3172],{"class":35,"line":1743},[33,3171,207],{"class":187},[33,3173,282],{"class":195},[33,3175,3176,3178],{"class":35,"line":1754},[33,3177,287],{"class":195},[33,3179,3180],{"class":290},"DataFetcher\n",[33,3182,3183,3186,3188],{"class":35,"line":1759},[33,3184,3185],{"class":191},"      url",[33,3187,220],{"class":187},[33,3189,3190],{"class":223},"\"\u002Fapi\u002Fusers\"\n",[33,3192,3193,3196,3198,3200,3203,3205,3207,3209,3211,3213,3215],{"class":35,"line":1765},[33,3194,3195],{"class":191},"      render",[33,3197,220],{"class":187},[33,3199,2865],{"class":195},[33,3201,3202],{"class":199},"status",[33,3204,387],{"class":195},[33,3206,3041],{"class":199},[33,3208,387],{"class":195},[33,3210,3082],{"class":199},[33,3212,2876],{"class":195},[33,3214,1557],{"class":187},[33,3216,2340],{"class":195},[33,3218,3219,3221,3224,3226,3229,3231,3234,3236,3239],{"class":35,"line":1771},[33,3220,2355],{"class":187},[33,3222,3223],{"class":195}," (status ",[33,3225,1511],{"class":187},[33,3227,3228],{"class":223}," 'loading'",[33,3230,2749],{"class":195},[33,3232,3233],{"class":187},"return",[33,3235,210],{"class":195},[33,3237,3238],{"class":290},"Spinner",[33,3240,3241],{"class":195}," \u002F>\n",[33,3243,3244,3246,3248,3250,3253,3255,3257,3259,3262,3265,3267],{"class":35,"line":1777},[33,3245,2355],{"class":187},[33,3247,3223],{"class":195},[33,3249,1511],{"class":187},[33,3251,3252],{"class":223}," 'error'",[33,3254,2749],{"class":195},[33,3256,3233],{"class":187},[33,3258,210],{"class":195},[33,3260,3261],{"class":290},"ErrorMessage",[33,3263,3264],{"class":191}," error",[33,3266,220],{"class":187},[33,3268,3269],{"class":195},"{error} \u002F>\n",[33,3271,3272,3274,3276,3279,3282,3284,3286,3289,3291,3293,3296,3298,3300,3303,3305,3308,3310],{"class":35,"line":1787},[33,3273,2415],{"class":187},[33,3275,210],{"class":195},[33,3277,3278],{"class":213},"ul",[33,3280,3281],{"class":195},">{data.",[33,3283,2048],{"class":191},[33,3285,1057],{"class":195},[33,3287,3288],{"class":199},"u",[33,3290,2056],{"class":187},[33,3292,210],{"class":195},[33,3294,3295],{"class":213},"li",[33,3297,2068],{"class":191},[33,3299,220],{"class":187},[33,3301,3302],{"class":195},"{u.id}>{u.name}\u003C\u002F",[33,3304,3295],{"class":213},[33,3306,3307],{"class":195},">)}\u003C\u002F",[33,3309,3278],{"class":213},[33,3311,232],{"class":195},[33,3313,3314],{"class":35,"line":1794},[33,3315,3316],{"class":195},"      }}\n",[33,3318,3319],{"class":35,"line":1812},[33,3320,558],{"class":195},[33,3322,3323],{"class":35,"line":1822},[33,3324,336],{"class":195},[33,3326,3327],{"class":35,"line":1845},[33,3328,237],{"class":195},[570,3330,3332,3333,3335],{"id":3331},"render-prop-via-children-the-idiomatic-form","Render Prop via ",[30,3334,154],{}," (the idiomatic form)",[19,3337,3339],{"filename":3338,"language":22},"render_prop_children.js",[24,3340,3342],{"className":26,"code":3341,"language":22,"meta":28,"style":28},"\u002F\u002F React community convention: pass the render function as `children` rather\n\u002F\u002F than a prop named `render`. This reads more naturally in JSX and is the form\n\u002F\u002F used by libraries like react-motion and downshift.\n\nfunction MouseTracker({ children }) {\n  const [pos, setPos] = useState({ x: 0, y: 0 })\n  useEffect(() => {\n    const handler = (e) => setPos({ x: e.clientX, y: e.clientY })\n    window.addEventListener('mousemove', handler)\n    return () => window.removeEventListener('mousemove', handler)\n  }, [])\n  return children(pos)  \u002F\u002F children is a FUNCTION here, not JSX\n}\n\n\u002F\u002F Usage — `children` is a function receiving the mouse position:\nfunction App() {\n  return (\n    \u003CMouseTracker>\n      {({ x, y }) => \u003Cp>Mouse at {x}, {y}\u003C\u002Fp>}\n    \u003C\u002FMouseTracker>\n  )\n}\n",[30,3343,3344,3349,3354,3359,3363,3375,3405,3415,3437,3450,3468,3472,3485,3489,3493,3498,3506,3512,3520,3547,3555,3559],{"__ignoreMap":28},[33,3345,3346],{"class":35,"line":36},[33,3347,3348],{"class":39},"\u002F\u002F React community convention: pass the render function as `children` rather\n",[33,3350,3351],{"class":35,"line":43},[33,3352,3353],{"class":39},"\u002F\u002F than a prop named `render`. This reads more naturally in JSX and is the form\n",[33,3355,3356],{"class":35,"line":49},[33,3357,3358],{"class":39},"\u002F\u002F used by libraries like react-motion and downshift.\n",[33,3360,3361],{"class":35,"line":55},[33,3362,65],{"emptyLinePlaceholder":64},[33,3364,3365,3367,3369,3371,3373],{"class":35,"line":61},[33,3366,188],{"class":187},[33,3368,2674],{"class":191},[33,3370,196],{"class":195},[33,3372,154],{"class":199},[33,3374,202],{"class":195},[33,3376,3377,3379,3381,3384,3386,3389,3391,3393,3395,3397,3399,3401,3403],{"class":35,"line":68},[33,3378,968],{"class":187},[33,3380,1273],{"class":195},[33,3382,3383],{"class":290},"pos",[33,3385,387],{"class":195},[33,3387,3388],{"class":290},"setPos",[33,3390,1284],{"class":195},[33,3392,220],{"class":187},[33,3394,1289],{"class":191},[33,3396,2704],{"class":195},[33,3398,2707],{"class":290},[33,3400,2710],{"class":195},[33,3402,2707],{"class":290},[33,3404,2715],{"class":195},[33,3406,3407,3409,3411,3413],{"class":35,"line":74},[33,3408,2724],{"class":191},[33,3410,2727],{"class":195},[33,3412,1557],{"class":187},[33,3414,2340],{"class":195},[33,3416,3417,3419,3422,3424,3426,3428,3430,3432,3435],{"class":35,"line":80},[33,3418,2736],{"class":187},[33,3420,3421],{"class":191}," handler",[33,3423,936],{"class":187},[33,3425,1452],{"class":195},[33,3427,2746],{"class":199},[33,3429,2749],{"class":195},[33,3431,1557],{"class":187},[33,3433,3434],{"class":191}," setPos",[33,3436,2757],{"class":195},[33,3438,3439,3441,3443,3445,3447],{"class":35,"line":86},[33,3440,2762],{"class":195},[33,3442,2765],{"class":191},[33,3444,1057],{"class":195},[33,3446,2770],{"class":223},[33,3448,3449],{"class":195},", handler)\n",[33,3451,3452,3454,3456,3458,3460,3462,3464,3466],{"class":35,"line":92},[33,3453,2778],{"class":187},[33,3455,2781],{"class":195},[33,3457,1557],{"class":187},[33,3459,2786],{"class":195},[33,3461,2789],{"class":191},[33,3463,1057],{"class":195},[33,3465,2770],{"class":223},[33,3467,3449],{"class":195},[33,3469,3470],{"class":35,"line":97},[33,3471,2800],{"class":195},[33,3473,3474,3476,3479,3482],{"class":35,"line":103},[33,3475,207],{"class":187},[33,3477,3478],{"class":191}," children",[33,3480,3481],{"class":195},"(pos)  ",[33,3483,3484],{"class":39},"\u002F\u002F children is a FUNCTION here, not JSX\n",[33,3486,3487],{"class":35,"line":109},[33,3488,237],{"class":195},[33,3490,3491],{"class":35,"line":115},[33,3492,65],{"emptyLinePlaceholder":64},[33,3494,3495],{"class":35,"line":121},[33,3496,3497],{"class":39},"\u002F\u002F Usage — `children` is a function receiving the mouse position:\n",[33,3499,3500,3502,3504],{"class":35,"line":127},[33,3501,188],{"class":187},[33,3503,505],{"class":191},[33,3505,275],{"class":195},[33,3507,3508,3510],{"class":35,"line":132},[33,3509,207],{"class":187},[33,3511,282],{"class":195},[33,3513,3514,3516,3518],{"class":35,"line":138},[33,3515,287],{"class":195},[33,3517,2858],{"class":290},[33,3519,232],{"class":195},[33,3521,3522,3525,3527,3529,3531,3533,3535,3537,3539,3542,3544],{"class":35,"line":144},[33,3523,3524],{"class":195},"      {({ ",[33,3526,2868],{"class":199},[33,3528,387],{"class":195},[33,3530,2873],{"class":199},[33,3532,2876],{"class":195},[33,3534,1557],{"class":187},[33,3536,210],{"class":195},[33,3538,314],{"class":213},[33,3540,3541],{"class":195},">Mouse at {x}, {y}\u003C\u002F",[33,3543,314],{"class":213},[33,3545,3546],{"class":195},">}\n",[33,3548,3549,3551,3553],{"class":35,"line":333},[33,3550,326],{"class":195},[33,3552,2858],{"class":290},[33,3554,232],{"class":195},[33,3556,3557],{"class":35,"line":339},[33,3558,336],{"class":195},[33,3560,3561],{"class":35,"line":523},[33,3562,237],{"class":195},[14,3564,3566],{"id":3565},"higher-order-components-hocs-legacy-enhancement","Higher-Order Components (HOCs): Legacy Enhancement",[19,3568,3570],{"filename":3569,"language":22},"hocs.js",[24,3571,3573],{"className":26,"code":3572,"language":22,"meta":28,"style":28},"\u002F\u002F HOCs are functions that take a component and return a new, enhanced one —\n\u002F\u002F the pre-hooks mechanism for adding cross-cutting behavior (auth-gating,\n\u002F\u002F logging, analytics) to many components. Still seen in older\u002Fenterprise\n\u002F\u002F codebases and some libraries, but largely superseded by hooks for new code.\n\nfunction withAuthGuard(Component) {\n  return function AuthGuarded(props) {\n    const { user } = useAuth()\n    if (!user) return \u003CLoginPrompt \u002F>\n    return \u003CComponent {...props} user={user} \u002F>\n  }\n}\n\nconst ProtectedDashboard = withAuthGuard(Dashboard)\n\n\u002F\u002F PROBLEM: stacking HOCs obscures the component tree and prop flow:\n\u002F\u002F   withAuth(withLogging(withTheme(withAnalytics(Dashboard))))\n\u002F\u002F Each layer adds a wrapper component with a generic name, making React\n\u002F\u002F DevTools' tree much harder to read, and props injected by inner HOCs are\n\u002F\u002F invisible at the call site.\n",[30,3574,3575,3580,3585,3590,3595,3599,3613,3629,3648,3669,3691,3696,3700,3704,3718,3722,3727,3732,3737,3742],{"__ignoreMap":28},[33,3576,3577],{"class":35,"line":36},[33,3578,3579],{"class":39},"\u002F\u002F HOCs are functions that take a component and return a new, enhanced one —\n",[33,3581,3582],{"class":35,"line":43},[33,3583,3584],{"class":39},"\u002F\u002F the pre-hooks mechanism for adding cross-cutting behavior (auth-gating,\n",[33,3586,3587],{"class":35,"line":49},[33,3588,3589],{"class":39},"\u002F\u002F logging, analytics) to many components. Still seen in older\u002Fenterprise\n",[33,3591,3592],{"class":35,"line":55},[33,3593,3594],{"class":39},"\u002F\u002F codebases and some libraries, but largely superseded by hooks for new code.\n",[33,3596,3597],{"class":35,"line":61},[33,3598,65],{"emptyLinePlaceholder":64},[33,3600,3601,3603,3606,3608,3611],{"class":35,"line":68},[33,3602,188],{"class":187},[33,3604,3605],{"class":191}," withAuthGuard",[33,3607,1057],{"class":195},[33,3609,3610],{"class":199},"Component",[33,3612,1062],{"class":195},[33,3614,3615,3617,3620,3623,3625,3627],{"class":35,"line":74},[33,3616,207],{"class":187},[33,3618,3619],{"class":187}," function",[33,3621,3622],{"class":191}," AuthGuarded",[33,3624,1057],{"class":195},[33,3626,961],{"class":199},[33,3628,1062],{"class":195},[33,3630,3631,3633,3635,3638,3640,3642,3645],{"class":35,"line":80},[33,3632,2736],{"class":187},[33,3634,1427],{"class":195},[33,3636,3637],{"class":290},"user",[33,3639,1436],{"class":195},[33,3641,220],{"class":187},[33,3643,3644],{"class":191}," useAuth",[33,3646,3647],{"class":195},"()\n",[33,3649,3650,3653,3655,3657,3660,3662,3664,3667],{"class":35,"line":86},[33,3651,3652],{"class":187},"    if",[33,3654,1452],{"class":195},[33,3656,1455],{"class":187},[33,3658,3659],{"class":195},"user) ",[33,3661,3233],{"class":187},[33,3663,210],{"class":195},[33,3665,3666],{"class":290},"LoginPrompt",[33,3668,3241],{"class":195},[33,3670,3671,3673,3675,3677,3679,3681,3684,3686,3688],{"class":35,"line":92},[33,3672,2778],{"class":187},[33,3674,210],{"class":195},[33,3676,3610],{"class":290},[33,3678,1082],{"class":195},[33,3680,958],{"class":187},[33,3682,3683],{"class":195},"props} ",[33,3685,3637],{"class":191},[33,3687,220],{"class":187},[33,3689,3690],{"class":195},"{user} \u002F>\n",[33,3692,3693],{"class":35,"line":97},[33,3694,3695],{"class":195},"  }\n",[33,3697,3698],{"class":35,"line":103},[33,3699,237],{"class":195},[33,3701,3702],{"class":35,"line":109},[33,3703,65],{"emptyLinePlaceholder":64},[33,3705,3706,3708,3711,3713,3715],{"class":35,"line":115},[33,3707,1228],{"class":187},[33,3709,3710],{"class":290}," ProtectedDashboard",[33,3712,936],{"class":187},[33,3714,3605],{"class":191},[33,3716,3717],{"class":195},"(Dashboard)\n",[33,3719,3720],{"class":35,"line":121},[33,3721,65],{"emptyLinePlaceholder":64},[33,3723,3724],{"class":35,"line":127},[33,3725,3726],{"class":39},"\u002F\u002F PROBLEM: stacking HOCs obscures the component tree and prop flow:\n",[33,3728,3729],{"class":35,"line":132},[33,3730,3731],{"class":39},"\u002F\u002F   withAuth(withLogging(withTheme(withAnalytics(Dashboard))))\n",[33,3733,3734],{"class":35,"line":138},[33,3735,3736],{"class":39},"\u002F\u002F Each layer adds a wrapper component with a generic name, making React\n",[33,3738,3739],{"class":35,"line":144},[33,3740,3741],{"class":39},"\u002F\u002F DevTools' tree much harder to read, and props injected by inner HOCs are\n",[33,3743,3744],{"class":35,"line":333},[33,3745,3746],{"class":39},"\u002F\u002F invisible at the call site.\n",[570,3748,3750],{"id":3749},"hoc-anti-patterns","HOC Anti-Patterns",[19,3752,3754],{"filename":3753,"language":22},"hoc_antipatterns.js",[24,3755,3757],{"className":26,"code":3756,"language":22,"meta":28,"style":28},"\u002F\u002F ANTI-PATTERN 1: Not forwarding refs — a plain HOC wrapper has no way to\n\u002F\u002F receive a `ref` passed to the enhanced component.\nfunction withThemeBad(Component) {\n  return function Themed(props) {\n    const theme = useContext(ThemeContext)\n    return \u003CComponent {...props} theme={theme} \u002F>\n    \u002F\u002F A ref passed to \u003CThemed ref={myRef}> goes NOWHERE — ref isn't a normal prop.\n  }\n}\n\n\u002F\u002F FIX: wrap with React.forwardRef\nfunction withThemeGood(Component) {\n  const Themed = React.forwardRef((props, ref) => {\n    const theme = useContext(ThemeContext)\n    return \u003CComponent {...props} ref={ref} theme={theme} \u002F>\n  })\n  Themed.displayName = `withTheme(${Component.displayName || Component.name})`\n  return Themed\n}\n\n\u002F\u002F ANTI-PATTERN 2: HOCs inside render — creating the enhanced component during\n\u002F\u002F render produces a NEW component type every render, causing full unmount\u002Fremount\n\u002F\u002F of the subtree (losing all state, triggering all effects).\nfunction BadUsage() {\n  \u002F\u002F This runs on EVERY render — a new component type each time → React unmounts\n  \u002F\u002F and remounts the entire subtree, destroying state and re-running effects.\n  const ProtectedDashboard = withAuthGuard(Dashboard)\n  return \u003CProtectedDashboard \u002F>\n}\n\n\u002F\u002F FIX: apply HOCs at module scope, not inside render\nconst ProtectedDashboard = withAuthGuard(Dashboard)  \u002F\u002F created ONCE\nfunction GoodUsage() {\n  return \u003CProtectedDashboard \u002F>\n}\n\n\u002F\u002F ANTI-PATTERN 3: Copying static methods — HOCs return a new component that\n\u002F\u002F doesn't have the original's static properties. Must hoist them explicitly.\nimport hoistNonReactStatics from 'hoist-non-react-statics'\nfunction withAnalytics(Component) {\n  const WithAnalytics = (props) => {\n    useEffect(() => { trackPageView() }, [])\n    return \u003CComponent {...props} \u002F>\n  }\n  hoistNonReactStatics(WithAnalytics, Component)  \u002F\u002F copies statics like .defaultProps\n  return WithAnalytics\n}\n",[30,3758,3759,3764,3769,3782,3797,3811,3833,3838,3842,3846,3850,3855,3868,3898,3910,3937,3942,3974,3981,3985,3989,3994,3999,4004,4013,4018,4023,4035,4046,4050,4054,4059,4075,4084,4094,4098,4102,4107,4112,4124,4137,4156,4173,4187,4191,4202,4209],{"__ignoreMap":28},[33,3760,3761],{"class":35,"line":36},[33,3762,3763],{"class":39},"\u002F\u002F ANTI-PATTERN 1: Not forwarding refs — a plain HOC wrapper has no way to\n",[33,3765,3766],{"class":35,"line":43},[33,3767,3768],{"class":39},"\u002F\u002F receive a `ref` passed to the enhanced component.\n",[33,3770,3771,3773,3776,3778,3780],{"class":35,"line":49},[33,3772,188],{"class":187},[33,3774,3775],{"class":191}," withThemeBad",[33,3777,1057],{"class":195},[33,3779,3610],{"class":199},[33,3781,1062],{"class":195},[33,3783,3784,3786,3788,3791,3793,3795],{"class":35,"line":55},[33,3785,207],{"class":187},[33,3787,3619],{"class":187},[33,3789,3790],{"class":191}," Themed",[33,3792,1057],{"class":195},[33,3794,961],{"class":199},[33,3796,1062],{"class":195},[33,3798,3799,3801,3804,3806,3808],{"class":35,"line":61},[33,3800,2736],{"class":187},[33,3802,3803],{"class":290}," theme",[33,3805,936],{"class":187},[33,3807,1441],{"class":191},[33,3809,3810],{"class":195},"(ThemeContext)\n",[33,3812,3813,3815,3817,3819,3821,3823,3825,3828,3830],{"class":35,"line":68},[33,3814,2778],{"class":187},[33,3816,210],{"class":195},[33,3818,3610],{"class":290},[33,3820,1082],{"class":195},[33,3822,958],{"class":187},[33,3824,3683],{"class":195},[33,3826,3827],{"class":191},"theme",[33,3829,220],{"class":187},[33,3831,3832],{"class":195},"{theme} \u002F>\n",[33,3834,3835],{"class":35,"line":74},[33,3836,3837],{"class":39},"    \u002F\u002F A ref passed to \u003CThemed ref={myRef}> goes NOWHERE — ref isn't a normal prop.\n",[33,3839,3840],{"class":35,"line":80},[33,3841,3695],{"class":195},[33,3843,3844],{"class":35,"line":86},[33,3845,237],{"class":195},[33,3847,3848],{"class":35,"line":92},[33,3849,65],{"emptyLinePlaceholder":64},[33,3851,3852],{"class":35,"line":97},[33,3853,3854],{"class":39},"\u002F\u002F FIX: wrap with React.forwardRef\n",[33,3856,3857,3859,3862,3864,3866],{"class":35,"line":103},[33,3858,188],{"class":187},[33,3860,3861],{"class":191}," withThemeGood",[33,3863,1057],{"class":195},[33,3865,3610],{"class":199},[33,3867,1062],{"class":195},[33,3869,3870,3872,3874,3876,3879,3882,3885,3887,3889,3892,3894,3896],{"class":35,"line":109},[33,3871,968],{"class":187},[33,3873,3790],{"class":290},[33,3875,936],{"class":187},[33,3877,3878],{"class":195}," React.",[33,3880,3881],{"class":191},"forwardRef",[33,3883,3884],{"class":195},"((",[33,3886,961],{"class":199},[33,3888,387],{"class":195},[33,3890,3891],{"class":199},"ref",[33,3893,2749],{"class":195},[33,3895,1557],{"class":187},[33,3897,2340],{"class":195},[33,3899,3900,3902,3904,3906,3908],{"class":35,"line":115},[33,3901,2736],{"class":187},[33,3903,3803],{"class":290},[33,3905,936],{"class":187},[33,3907,1441],{"class":191},[33,3909,3810],{"class":195},[33,3911,3912,3914,3916,3918,3920,3922,3924,3926,3928,3931,3933,3935],{"class":35,"line":121},[33,3913,2778],{"class":187},[33,3915,210],{"class":195},[33,3917,3610],{"class":290},[33,3919,1082],{"class":195},[33,3921,958],{"class":187},[33,3923,3683],{"class":195},[33,3925,3891],{"class":191},[33,3927,220],{"class":187},[33,3929,3930],{"class":195},"{ref} ",[33,3932,3827],{"class":191},[33,3934,220],{"class":187},[33,3936,3832],{"class":195},[33,3938,3939],{"class":35,"line":127},[33,3940,3941],{"class":195},"  })\n",[33,3943,3944,3947,3949,3952,3954,3957,3960,3963,3966,3968,3971],{"class":35,"line":132},[33,3945,3946],{"class":195},"  Themed.displayName ",[33,3948,220],{"class":187},[33,3950,3951],{"class":223}," `withTheme(${",[33,3953,3610],{"class":195},[33,3955,3956],{"class":223},".",[33,3958,3959],{"class":195},"displayName",[33,3961,3962],{"class":187}," ||",[33,3964,3965],{"class":195}," Component",[33,3967,3956],{"class":223},[33,3969,3970],{"class":195},"name",[33,3972,3973],{"class":223},"})`\n",[33,3975,3976,3978],{"class":35,"line":138},[33,3977,207],{"class":187},[33,3979,3980],{"class":195}," Themed\n",[33,3982,3983],{"class":35,"line":144},[33,3984,237],{"class":195},[33,3986,3987],{"class":35,"line":333},[33,3988,65],{"emptyLinePlaceholder":64},[33,3990,3991],{"class":35,"line":339},[33,3992,3993],{"class":39},"\u002F\u002F ANTI-PATTERN 2: HOCs inside render — creating the enhanced component during\n",[33,3995,3996],{"class":35,"line":523},[33,3997,3998],{"class":39},"\u002F\u002F render produces a NEW component type every render, causing full unmount\u002Fremount\n",[33,4000,4001],{"class":35,"line":540},[33,4002,4003],{"class":39},"\u002F\u002F of the subtree (losing all state, triggering all effects).\n",[33,4005,4006,4008,4011],{"class":35,"line":555},[33,4007,188],{"class":187},[33,4009,4010],{"class":191}," BadUsage",[33,4012,275],{"class":195},[33,4014,4015],{"class":35,"line":561},[33,4016,4017],{"class":39},"  \u002F\u002F This runs on EVERY render — a new component type each time → React unmounts\n",[33,4019,4020],{"class":35,"line":566},[33,4021,4022],{"class":39},"  \u002F\u002F and remounts the entire subtree, destroying state and re-running effects.\n",[33,4024,4025,4027,4029,4031,4033],{"class":35,"line":1162},[33,4026,968],{"class":187},[33,4028,3710],{"class":290},[33,4030,936],{"class":187},[33,4032,3605],{"class":191},[33,4034,3717],{"class":195},[33,4036,4037,4039,4041,4044],{"class":35,"line":1167},[33,4038,207],{"class":187},[33,4040,210],{"class":195},[33,4042,4043],{"class":290},"ProtectedDashboard",[33,4045,3241],{"class":195},[33,4047,4048],{"class":35,"line":1500},[33,4049,237],{"class":195},[33,4051,4052],{"class":35,"line":1517},[33,4053,65],{"emptyLinePlaceholder":64},[33,4055,4056],{"class":35,"line":1546},[33,4057,4058],{"class":39},"\u002F\u002F FIX: apply HOCs at module scope, not inside render\n",[33,4060,4061,4063,4065,4067,4069,4072],{"class":35,"line":1566},[33,4062,1228],{"class":187},[33,4064,3710],{"class":290},[33,4066,936],{"class":187},[33,4068,3605],{"class":191},[33,4070,4071],{"class":195},"(Dashboard)  ",[33,4073,4074],{"class":39},"\u002F\u002F created ONCE\n",[33,4076,4077,4079,4082],{"class":35,"line":1572},[33,4078,188],{"class":187},[33,4080,4081],{"class":191}," GoodUsage",[33,4083,275],{"class":195},[33,4085,4086,4088,4090,4092],{"class":35,"line":1578},[33,4087,207],{"class":187},[33,4089,210],{"class":195},[33,4091,4043],{"class":290},[33,4093,3241],{"class":195},[33,4095,4096],{"class":35,"line":1587},[33,4097,237],{"class":195},[33,4099,4100],{"class":35,"line":1592},[33,4101,65],{"emptyLinePlaceholder":64},[33,4103,4104],{"class":35,"line":1597},[33,4105,4106],{"class":39},"\u002F\u002F ANTI-PATTERN 3: Copying static methods — HOCs return a new component that\n",[33,4108,4109],{"class":35,"line":1602},[33,4110,4111],{"class":39},"\u002F\u002F doesn't have the original's static properties. Must hoist them explicitly.\n",[33,4113,4114,4116,4119,4121],{"class":35,"line":1620},[33,4115,1210],{"class":187},[33,4117,4118],{"class":195}," hoistNonReactStatics ",[33,4120,1216],{"class":187},[33,4122,4123],{"class":223}," 'hoist-non-react-statics'\n",[33,4125,4126,4128,4131,4133,4135],{"class":35,"line":1637},[33,4127,188],{"class":187},[33,4129,4130],{"class":191}," withAnalytics",[33,4132,1057],{"class":195},[33,4134,3610],{"class":199},[33,4136,1062],{"class":195},[33,4138,4139,4141,4144,4146,4148,4150,4152,4154],{"class":35,"line":1661},[33,4140,968],{"class":187},[33,4142,4143],{"class":191}," WithAnalytics",[33,4145,936],{"class":187},[33,4147,1452],{"class":195},[33,4149,961],{"class":199},[33,4151,2749],{"class":195},[33,4153,1557],{"class":187},[33,4155,2340],{"class":195},[33,4157,4158,4161,4163,4165,4167,4170],{"class":35,"line":1699},[33,4159,4160],{"class":191},"    useEffect",[33,4162,2727],{"class":195},[33,4164,1557],{"class":187},[33,4166,1427],{"class":195},[33,4168,4169],{"class":191},"trackPageView",[33,4171,4172],{"class":195},"() }, [])\n",[33,4174,4175,4177,4179,4181,4183,4185],{"class":35,"line":1704},[33,4176,2778],{"class":187},[33,4178,210],{"class":195},[33,4180,3610],{"class":290},[33,4182,1082],{"class":195},[33,4184,958],{"class":187},[33,4186,1087],{"class":195},[33,4188,4189],{"class":35,"line":1709},[33,4190,3695],{"class":195},[33,4192,4193,4196,4199],{"class":35,"line":1715},[33,4194,4195],{"class":191},"  hoistNonReactStatics",[33,4197,4198],{"class":195},"(WithAnalytics, Component)  ",[33,4200,4201],{"class":39},"\u002F\u002F copies statics like .defaultProps\n",[33,4203,4204,4206],{"class":35,"line":1721},[33,4205,207],{"class":187},[33,4207,4208],{"class":195}," WithAnalytics\n",[33,4210,4211],{"class":35,"line":1732},[33,4212,237],{"class":195},[14,4214,4216],{"id":4215},"hooks-as-the-modern-composition-mechanism","Hooks as the Modern Composition Mechanism",[19,4218,4220],{"filename":4219,"language":22},"hooks_composition.js",[24,4221,4223],{"className":26,"code":4222,"language":22,"meta":28,"style":28},"\u002F\u002F The hook equivalent of an HOC for cross-cutting LOGIC (not rendering): a hook\n\u002F\u002F + an explicit early return. No extra wrapper component in the tree, no ref\n\u002F\u002F forwarding issues, no static method hoisting, full visibility in DevTools.\n\n\u002F\u002F HOC approach (legacy):\nfunction withAuth(Component) {\n  return function AuthGuarded(props) {\n    const { user } = useAuth()\n    if (!user) return \u003CLoginPrompt \u002F>\n    return \u003CComponent {...props} user={user} \u002F>\n  }\n}\nconst ProtectedDashboard = withAuth(Dashboard)\n\n\u002F\u002F Hook approach (modern) — same behavior, no wrapper layer:\nfunction Dashboard() {\n  const { user } = useAuth()\n  if (!user) return \u003CLoginPrompt \u002F>\n  return \u003CDashboardContent user={user} \u002F>\n}\n\n\u002F\u002F WHY HOOKS REPLACE HOCs FOR LOGIC SHARING:\n\u002F\u002F - No extra component in the tree → cleaner DevTools, fewer re-renders\n\u002F\u002F - No ref forwarding or static method hoisting needed\n\u002F\u002F - Hook return values are explicit at the call site (not injected props)\n\u002F\u002F - Composable: useAuth() + useTheme() + useAnalytics() stacked naturally\n\u002F\u002F\n\u002F\u002F WHEN HOCs ARE STILL RELEVANT:\n\u002F\u002F - Injecting props into a component you DON'T control (third-party)\n\u002F\u002F - Code that predates hooks and hasn't been migrated\n\u002F\u002F - Some prop-injection library patterns (e.g. relay's fragment containers)\n",[30,4224,4225,4230,4235,4240,4244,4249,4262,4276,4292,4310,4330,4334,4338,4350,4354,4359,4367,4383,4401,4417,4421,4425,4430,4435,4440,4445,4450,4455,4460,4465,4470],{"__ignoreMap":28},[33,4226,4227],{"class":35,"line":36},[33,4228,4229],{"class":39},"\u002F\u002F The hook equivalent of an HOC for cross-cutting LOGIC (not rendering): a hook\n",[33,4231,4232],{"class":35,"line":43},[33,4233,4234],{"class":39},"\u002F\u002F + an explicit early return. No extra wrapper component in the tree, no ref\n",[33,4236,4237],{"class":35,"line":49},[33,4238,4239],{"class":39},"\u002F\u002F forwarding issues, no static method hoisting, full visibility in DevTools.\n",[33,4241,4242],{"class":35,"line":55},[33,4243,65],{"emptyLinePlaceholder":64},[33,4245,4246],{"class":35,"line":61},[33,4247,4248],{"class":39},"\u002F\u002F HOC approach (legacy):\n",[33,4250,4251,4253,4256,4258,4260],{"class":35,"line":68},[33,4252,188],{"class":187},[33,4254,4255],{"class":191}," withAuth",[33,4257,1057],{"class":195},[33,4259,3610],{"class":199},[33,4261,1062],{"class":195},[33,4263,4264,4266,4268,4270,4272,4274],{"class":35,"line":74},[33,4265,207],{"class":187},[33,4267,3619],{"class":187},[33,4269,3622],{"class":191},[33,4271,1057],{"class":195},[33,4273,961],{"class":199},[33,4275,1062],{"class":195},[33,4277,4278,4280,4282,4284,4286,4288,4290],{"class":35,"line":80},[33,4279,2736],{"class":187},[33,4281,1427],{"class":195},[33,4283,3637],{"class":290},[33,4285,1436],{"class":195},[33,4287,220],{"class":187},[33,4289,3644],{"class":191},[33,4291,3647],{"class":195},[33,4293,4294,4296,4298,4300,4302,4304,4306,4308],{"class":35,"line":86},[33,4295,3652],{"class":187},[33,4297,1452],{"class":195},[33,4299,1455],{"class":187},[33,4301,3659],{"class":195},[33,4303,3233],{"class":187},[33,4305,210],{"class":195},[33,4307,3666],{"class":290},[33,4309,3241],{"class":195},[33,4311,4312,4314,4316,4318,4320,4322,4324,4326,4328],{"class":35,"line":92},[33,4313,2778],{"class":187},[33,4315,210],{"class":195},[33,4317,3610],{"class":290},[33,4319,1082],{"class":195},[33,4321,958],{"class":187},[33,4323,3683],{"class":195},[33,4325,3637],{"class":191},[33,4327,220],{"class":187},[33,4329,3690],{"class":195},[33,4331,4332],{"class":35,"line":97},[33,4333,3695],{"class":195},[33,4335,4336],{"class":35,"line":103},[33,4337,237],{"class":195},[33,4339,4340,4342,4344,4346,4348],{"class":35,"line":109},[33,4341,1228],{"class":187},[33,4343,3710],{"class":290},[33,4345,936],{"class":187},[33,4347,4255],{"class":191},[33,4349,3717],{"class":195},[33,4351,4352],{"class":35,"line":115},[33,4353,65],{"emptyLinePlaceholder":64},[33,4355,4356],{"class":35,"line":121},[33,4357,4358],{"class":39},"\u002F\u002F Hook approach (modern) — same behavior, no wrapper layer:\n",[33,4360,4361,4363,4365],{"class":35,"line":127},[33,4362,188],{"class":187},[33,4364,272],{"class":191},[33,4366,275],{"class":195},[33,4368,4369,4371,4373,4375,4377,4379,4381],{"class":35,"line":132},[33,4370,968],{"class":187},[33,4372,1427],{"class":195},[33,4374,3637],{"class":290},[33,4376,1436],{"class":195},[33,4378,220],{"class":187},[33,4380,3644],{"class":191},[33,4382,3647],{"class":195},[33,4384,4385,4387,4389,4391,4393,4395,4397,4399],{"class":35,"line":138},[33,4386,1449],{"class":187},[33,4388,1452],{"class":195},[33,4390,1455],{"class":187},[33,4392,3659],{"class":195},[33,4394,3233],{"class":187},[33,4396,210],{"class":195},[33,4398,3666],{"class":290},[33,4400,3241],{"class":195},[33,4402,4403,4405,4407,4410,4413,4415],{"class":35,"line":144},[33,4404,207],{"class":187},[33,4406,210],{"class":195},[33,4408,4409],{"class":290},"DashboardContent",[33,4411,4412],{"class":191}," user",[33,4414,220],{"class":187},[33,4416,3690],{"class":195},[33,4418,4419],{"class":35,"line":333},[33,4420,237],{"class":195},[33,4422,4423],{"class":35,"line":339},[33,4424,65],{"emptyLinePlaceholder":64},[33,4426,4427],{"class":35,"line":523},[33,4428,4429],{"class":39},"\u002F\u002F WHY HOOKS REPLACE HOCs FOR LOGIC SHARING:\n",[33,4431,4432],{"class":35,"line":540},[33,4433,4434],{"class":39},"\u002F\u002F - No extra component in the tree → cleaner DevTools, fewer re-renders\n",[33,4436,4437],{"class":35,"line":555},[33,4438,4439],{"class":39},"\u002F\u002F - No ref forwarding or static method hoisting needed\n",[33,4441,4442],{"class":35,"line":561},[33,4443,4444],{"class":39},"\u002F\u002F - Hook return values are explicit at the call site (not injected props)\n",[33,4446,4447],{"class":35,"line":566},[33,4448,4449],{"class":39},"\u002F\u002F - Composable: useAuth() + useTheme() + useAnalytics() stacked naturally\n",[33,4451,4452],{"class":35,"line":1162},[33,4453,4454],{"class":39},"\u002F\u002F\n",[33,4456,4457],{"class":35,"line":1167},[33,4458,4459],{"class":39},"\u002F\u002F WHEN HOCs ARE STILL RELEVANT:\n",[33,4461,4462],{"class":35,"line":1500},[33,4463,4464],{"class":39},"\u002F\u002F - Injecting props into a component you DON'T control (third-party)\n",[33,4466,4467],{"class":35,"line":1517},[33,4468,4469],{"class":39},"\u002F\u002F - Code that predates hooks and hasn't been migrated\n",[33,4471,4472],{"class":35,"line":1546},[33,4473,4474],{"class":39},"\u002F\u002F - Some prop-injection library patterns (e.g. relay's fragment containers)\n",[14,4476,4478],{"id":4477},"slots-pattern-formalizing-named-composition","Slots Pattern: Formalizing Named Composition",[19,4480,4482],{"filename":4481,"language":22},"slots_pattern.js",[24,4483,4485],{"className":26,"code":4484,"language":22,"meta":28,"style":28},"\u002F\u002F The \"slots\" pattern generalizes `children` to multiple named insertion points.\n\u002F\u002F Two idioms in practice:\n\n\u002F\u002F IDIOM 1: JSX-as-prop (simplest, no extra abstraction)\nfunction PageLayout({ header, sidebar, main, footer }) {\n  return (\n    \u003Cdiv className=\"page\">\n      \u003Cheader>{header}\u003C\u002Fheader>\n      \u003Cdiv className=\"body\">\n        \u003Caside>{sidebar}\u003C\u002Faside>\n        \u003Cmain>{main}\u003C\u002Fmain>\n      \u003C\u002Fdiv>\n      \u003Cfooter>{footer}\u003C\u002Ffooter>\n    \u003C\u002Fdiv>\n  )\n}\n\nfunction App() {\n  return (\n    \u003CPageLayout\n      header={\u003CTopNav \u002F>}\n      sidebar={\u003CSideMenu \u002F>}\n      main={\u003CDashboard \u002F>}\n      footer={\u003CCopyrightNotice \u002F>}\n    \u002F>\n  )\n}\n\n\u002F\u002F IDIOM 2: Component-as-prop (render functions for lazy evaluation)\n\u002F\u002F Pass a COMPONENT type (not JSX) so the layout decides WHEN to render it —\n\u002F\u002F avoids rendering the slot content if it's not shown (e.g. collapsed sidebar).\nfunction LazyLayout({ header: Header, sidebar: Sidebar, main: Main }) {\n  const [sidebarOpen, setSidebarOpen] = useState(true)\n  return (\n    \u003Cdiv className=\"page\">\n      {Header && \u003CHeader \u002F>}\n      \u003Cdiv className=\"body\">\n        {sidebarOpen && Sidebar && \u003CSidebar \u002F>}\n        \u003Cmain>{Main && \u003CMain \u002F>}\u003C\u002Fmain>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  )\n}\n\n\u002F\u002F Usage — pass component types, not JSX elements:\n;\u003CLazyLayout\n  header={() => \u003CTopNav \u002F>}\n  sidebar={SideMenu}\n  main={Dashboard}\n\u002F>\n",[30,4486,4487,4492,4497,4501,4506,4533,4539,4554,4567,4581,4593,4605,4613,4626,4634,4638,4642,4646,4654,4660,4667,4681,4694,4706,4720,4724,4728,4732,4736,4741,4746,4751,4788,4815,4821,4835,4849,4863,4881,4903,4911,4919,4923,4927,4931,4936,4945,4955,4965,4975],{"__ignoreMap":28},[33,4488,4489],{"class":35,"line":36},[33,4490,4491],{"class":39},"\u002F\u002F The \"slots\" pattern generalizes `children` to multiple named insertion points.\n",[33,4493,4494],{"class":35,"line":43},[33,4495,4496],{"class":39},"\u002F\u002F Two idioms in practice:\n",[33,4498,4499],{"class":35,"line":49},[33,4500,65],{"emptyLinePlaceholder":64},[33,4502,4503],{"class":35,"line":55},[33,4504,4505],{"class":39},"\u002F\u002F IDIOM 1: JSX-as-prop (simplest, no extra abstraction)\n",[33,4507,4508,4510,4513,4515,4518,4520,4522,4524,4526,4528,4531],{"class":35,"line":61},[33,4509,188],{"class":187},[33,4511,4512],{"class":191}," PageLayout",[33,4514,196],{"class":195},[33,4516,4517],{"class":199},"header",[33,4519,387],{"class":195},[33,4521,384],{"class":199},[33,4523,387],{"class":195},[33,4525,390],{"class":199},[33,4527,387],{"class":195},[33,4529,4530],{"class":199},"footer",[33,4532,202],{"class":195},[33,4534,4535,4537],{"class":35,"line":68},[33,4536,207],{"class":187},[33,4538,282],{"class":195},[33,4540,4541,4543,4545,4547,4549,4552],{"class":35,"line":74},[33,4542,287],{"class":195},[33,4544,214],{"class":213},[33,4546,217],{"class":191},[33,4548,220],{"class":187},[33,4550,4551],{"class":223},"\"page\"",[33,4553,232],{"class":195},[33,4555,4556,4558,4560,4563,4565],{"class":35,"line":80},[33,4557,298],{"class":195},[33,4559,4517],{"class":213},[33,4561,4562],{"class":195},">{header}\u003C\u002F",[33,4564,4517],{"class":213},[33,4566,232],{"class":195},[33,4568,4569,4571,4573,4575,4577,4579],{"class":35,"line":86},[33,4570,298],{"class":195},[33,4572,214],{"class":213},[33,4574,217],{"class":191},[33,4576,220],{"class":187},[33,4578,703],{"class":223},[33,4580,232],{"class":195},[33,4582,4583,4585,4587,4589,4591],{"class":35,"line":92},[33,4584,1825],{"class":195},[33,4586,420],{"class":213},[33,4588,430],{"class":195},[33,4590,420],{"class":213},[33,4592,232],{"class":195},[33,4594,4595,4597,4599,4601,4603],{"class":35,"line":97},[33,4596,1825],{"class":195},[33,4598,390],{"class":213},[33,4600,450],{"class":195},[33,4602,390],{"class":213},[33,4604,232],{"class":195},[33,4606,4607,4609,4611],{"class":35,"line":103},[33,4608,1869],{"class":195},[33,4610,214],{"class":213},[33,4612,232],{"class":195},[33,4614,4615,4617,4619,4622,4624],{"class":35,"line":109},[33,4616,298],{"class":195},[33,4618,4530],{"class":213},[33,4620,4621],{"class":195},">{footer}\u003C\u002F",[33,4623,4530],{"class":213},[33,4625,232],{"class":195},[33,4627,4628,4630,4632],{"class":35,"line":115},[33,4629,326],{"class":195},[33,4631,214],{"class":213},[33,4633,232],{"class":195},[33,4635,4636],{"class":35,"line":121},[33,4637,336],{"class":195},[33,4639,4640],{"class":35,"line":127},[33,4641,237],{"class":195},[33,4643,4644],{"class":35,"line":132},[33,4645,65],{"emptyLinePlaceholder":64},[33,4647,4648,4650,4652],{"class":35,"line":138},[33,4649,188],{"class":187},[33,4651,505],{"class":191},[33,4653,275],{"class":195},[33,4655,4656,4658],{"class":35,"line":144},[33,4657,207],{"class":187},[33,4659,282],{"class":195},[33,4661,4662,4664],{"class":35,"line":333},[33,4663,287],{"class":195},[33,4665,4666],{"class":290},"PageLayout\n",[33,4668,4669,4672,4674,4676,4679],{"class":35,"line":339},[33,4670,4671],{"class":191},"      header",[33,4673,220],{"class":187},[33,4675,531],{"class":195},[33,4677,4678],{"class":290},"TopNav",[33,4680,537],{"class":195},[33,4682,4683,4685,4687,4689,4692],{"class":35,"line":523},[33,4684,526],{"class":191},[33,4686,220],{"class":187},[33,4688,531],{"class":195},[33,4690,4691],{"class":290},"SideMenu",[33,4693,537],{"class":195},[33,4695,4696,4698,4700,4702,4704],{"class":35,"line":540},[33,4697,543],{"class":191},[33,4699,220],{"class":187},[33,4701,531],{"class":195},[33,4703,550],{"class":290},[33,4705,537],{"class":195},[33,4707,4708,4711,4713,4715,4718],{"class":35,"line":555},[33,4709,4710],{"class":191},"      footer",[33,4712,220],{"class":187},[33,4714,531],{"class":195},[33,4716,4717],{"class":290},"CopyrightNotice",[33,4719,537],{"class":195},[33,4721,4722],{"class":35,"line":561},[33,4723,558],{"class":195},[33,4725,4726],{"class":35,"line":566},[33,4727,336],{"class":195},[33,4729,4730],{"class":35,"line":1162},[33,4731,237],{"class":195},[33,4733,4734],{"class":35,"line":1167},[33,4735,65],{"emptyLinePlaceholder":64},[33,4737,4738],{"class":35,"line":1500},[33,4739,4740],{"class":39},"\u002F\u002F IDIOM 2: Component-as-prop (render functions for lazy evaluation)\n",[33,4742,4743],{"class":35,"line":1517},[33,4744,4745],{"class":39},"\u002F\u002F Pass a COMPONENT type (not JSX) so the layout decides WHEN to render it —\n",[33,4747,4748],{"class":35,"line":1546},[33,4749,4750],{"class":39},"\u002F\u002F avoids rendering the slot content if it's not shown (e.g. collapsed sidebar).\n",[33,4752,4753,4755,4758,4760,4762,4765,4768,4770,4772,4774,4777,4779,4781,4783,4786],{"class":35,"line":1566},[33,4754,188],{"class":187},[33,4756,4757],{"class":191}," LazyLayout",[33,4759,196],{"class":195},[33,4761,4517],{"class":199},[33,4763,4764],{"class":195},": ",[33,4766,4767],{"class":199},"Header",[33,4769,387],{"class":195},[33,4771,384],{"class":199},[33,4773,4764],{"class":195},[33,4775,4776],{"class":199},"Sidebar",[33,4778,387],{"class":195},[33,4780,390],{"class":199},[33,4782,4764],{"class":195},[33,4784,4785],{"class":199},"Main",[33,4787,202],{"class":195},[33,4789,4790,4792,4794,4797,4799,4802,4804,4806,4808,4810,4813],{"class":35,"line":1572},[33,4791,968],{"class":187},[33,4793,1273],{"class":195},[33,4795,4796],{"class":290},"sidebarOpen",[33,4798,387],{"class":195},[33,4800,4801],{"class":290},"setSidebarOpen",[33,4803,1284],{"class":195},[33,4805,220],{"class":187},[33,4807,1289],{"class":191},[33,4809,1057],{"class":195},[33,4811,4812],{"class":290},"true",[33,4814,1244],{"class":195},[33,4816,4817,4819],{"class":35,"line":1578},[33,4818,207],{"class":187},[33,4820,282],{"class":195},[33,4822,4823,4825,4827,4829,4831,4833],{"class":35,"line":1587},[33,4824,287],{"class":195},[33,4826,214],{"class":213},[33,4828,217],{"class":191},[33,4830,220],{"class":187},[33,4832,4551],{"class":223},[33,4834,232],{"class":195},[33,4836,4837,4840,4843,4845,4847],{"class":35,"line":1592},[33,4838,4839],{"class":195},"      {Header ",[33,4841,4842],{"class":187},"&&",[33,4844,210],{"class":195},[33,4846,4767],{"class":290},[33,4848,537],{"class":195},[33,4850,4851,4853,4855,4857,4859,4861],{"class":35,"line":1597},[33,4852,298],{"class":195},[33,4854,214],{"class":213},[33,4856,217],{"class":191},[33,4858,220],{"class":187},[33,4860,703],{"class":223},[33,4862,232],{"class":195},[33,4864,4865,4868,4870,4873,4875,4877,4879],{"class":35,"line":1602},[33,4866,4867],{"class":195},"        {sidebarOpen ",[33,4869,4842],{"class":187},[33,4871,4872],{"class":195}," Sidebar ",[33,4874,4842],{"class":187},[33,4876,210],{"class":195},[33,4878,4776],{"class":290},[33,4880,537],{"class":195},[33,4882,4883,4885,4887,4890,4892,4894,4896,4899,4901],{"class":35,"line":1620},[33,4884,1825],{"class":195},[33,4886,390],{"class":213},[33,4888,4889],{"class":195},">{Main ",[33,4891,4842],{"class":187},[33,4893,210],{"class":195},[33,4895,4785],{"class":290},[33,4897,4898],{"class":195}," \u002F>}\u003C\u002F",[33,4900,390],{"class":213},[33,4902,232],{"class":195},[33,4904,4905,4907,4909],{"class":35,"line":1637},[33,4906,1869],{"class":195},[33,4908,214],{"class":213},[33,4910,232],{"class":195},[33,4912,4913,4915,4917],{"class":35,"line":1661},[33,4914,326],{"class":195},[33,4916,214],{"class":213},[33,4918,232],{"class":195},[33,4920,4921],{"class":35,"line":1699},[33,4922,336],{"class":195},[33,4924,4925],{"class":35,"line":1704},[33,4926,237],{"class":195},[33,4928,4929],{"class":35,"line":1709},[33,4930,65],{"emptyLinePlaceholder":64},[33,4932,4933],{"class":35,"line":1715},[33,4934,4935],{"class":39},"\u002F\u002F Usage — pass component types, not JSX elements:\n",[33,4937,4938,4940,4942],{"class":35,"line":1721},[33,4939,780],{"class":195},[33,4941,783],{"class":187},[33,4943,4944],{"class":195},"LazyLayout\n",[33,4946,4947,4950,4952],{"class":35,"line":1732},[33,4948,4949],{"class":195},"  header",[33,4951,220],{"class":187},[33,4953,4954],{"class":195},"{() => \u003CTopNav \u002F>}\n",[33,4956,4957,4960,4962],{"class":35,"line":1743},[33,4958,4959],{"class":195},"  sidebar",[33,4961,220],{"class":187},[33,4963,4964],{"class":195},"{SideMenu}\n",[33,4966,4967,4970,4972],{"class":35,"line":1754},[33,4968,4969],{"class":195},"  main",[33,4971,220],{"class":187},[33,4973,4974],{"class":195},"{Dashboard}\n",[33,4976,4977],{"class":35,"line":1759},[33,4978,885],{"class":187},[14,4980,4982],{"id":4981},"tips-tricks","💡 Tips & Tricks",[19,4984,4986],{"filename":4985,"language":22},"tips.js",[24,4987,4989],{"className":26,"code":4988,"language":22,"meta":28,"style":28},"\u002F\u002F [Idiom] Reach for `children` as the DEFAULT composition mechanism for \"wrapper\n\u002F\u002F with arbitrary content inside\" (cards, modals, layout containers). Reach for\n\u002F\u002F named slot props only once a component genuinely needs MORE than one content region.\n\n\u002F\u002F [Idiom] Build \"specialized\" variants as thin wrapper functions that forward\n\u002F\u002F `...props` and override only the differing prop — this gives the specialized\n\u002F\u002F variant every future capability the base component gains, with zero maintenance.\n\n\u002F\u002F [Idiom] Reach for compound components specifically when several components\n\u002F\u002F need to share implicit state AND the caller should retain control over\n\u002F\u002F arrangement. For a fixed, non-rearrangeable structure, a single component with\n\u002F\u002F a data prop (the rigid alternative) is simpler and often better.\n\n\u002F\u002F [Debug] If a compound component's sub-components throw when used outside their\n\u002F\u002F parent, it's because useContext returned the default (null). Add an explicit\n\u002F\u002F check that throws a clear \"must be used inside \u003CTabs>\" error rather than\n\u002F\u002F letting a cryptic \"cannot read property of null\" surface.\n\n\u002F\u002F [Performance] Render props create a new function every render — wrap the\n\u002F\u002F passed function in useCallback if it's passed to a memoized child, or the\n\u002F\u002F child re-renders every time even if its inputs haven't changed.\n\n\u002F\u002F [Idiom] Prefer custom hooks over HOCs for sharing cross-cutting LOGIC in new\n\u002F\u002F code. HOCs remain relevant mainly in older codebases and prop-injection\n\u002F\u002F library patterns that predate hooks.\n\n\u002F\u002F [Idiom] When using `children` as a render prop, name the prop explicitly\n\u002F\u002F (`render` or `children`) and document that it receives a function — a caller\n\u002F\u002F passing JSX instead of a function will see \"children is not a function\" with\n\u002F\u002F no obvious explanation.\n",[30,4990,4991,4996,5001,5006,5010,5015,5020,5025,5029,5034,5039,5044,5049,5053,5058,5063,5068,5073,5077,5082,5087,5092,5096,5101,5106,5111,5115,5120,5125,5130],{"__ignoreMap":28},[33,4992,4993],{"class":35,"line":36},[33,4994,4995],{"class":39},"\u002F\u002F [Idiom] Reach for `children` as the DEFAULT composition mechanism for \"wrapper\n",[33,4997,4998],{"class":35,"line":43},[33,4999,5000],{"class":39},"\u002F\u002F with arbitrary content inside\" (cards, modals, layout containers). Reach for\n",[33,5002,5003],{"class":35,"line":49},[33,5004,5005],{"class":39},"\u002F\u002F named slot props only once a component genuinely needs MORE than one content region.\n",[33,5007,5008],{"class":35,"line":55},[33,5009,65],{"emptyLinePlaceholder":64},[33,5011,5012],{"class":35,"line":61},[33,5013,5014],{"class":39},"\u002F\u002F [Idiom] Build \"specialized\" variants as thin wrapper functions that forward\n",[33,5016,5017],{"class":35,"line":68},[33,5018,5019],{"class":39},"\u002F\u002F `...props` and override only the differing prop — this gives the specialized\n",[33,5021,5022],{"class":35,"line":74},[33,5023,5024],{"class":39},"\u002F\u002F variant every future capability the base component gains, with zero maintenance.\n",[33,5026,5027],{"class":35,"line":80},[33,5028,65],{"emptyLinePlaceholder":64},[33,5030,5031],{"class":35,"line":86},[33,5032,5033],{"class":39},"\u002F\u002F [Idiom] Reach for compound components specifically when several components\n",[33,5035,5036],{"class":35,"line":92},[33,5037,5038],{"class":39},"\u002F\u002F need to share implicit state AND the caller should retain control over\n",[33,5040,5041],{"class":35,"line":97},[33,5042,5043],{"class":39},"\u002F\u002F arrangement. For a fixed, non-rearrangeable structure, a single component with\n",[33,5045,5046],{"class":35,"line":103},[33,5047,5048],{"class":39},"\u002F\u002F a data prop (the rigid alternative) is simpler and often better.\n",[33,5050,5051],{"class":35,"line":109},[33,5052,65],{"emptyLinePlaceholder":64},[33,5054,5055],{"class":35,"line":115},[33,5056,5057],{"class":39},"\u002F\u002F [Debug] If a compound component's sub-components throw when used outside their\n",[33,5059,5060],{"class":35,"line":121},[33,5061,5062],{"class":39},"\u002F\u002F parent, it's because useContext returned the default (null). Add an explicit\n",[33,5064,5065],{"class":35,"line":127},[33,5066,5067],{"class":39},"\u002F\u002F check that throws a clear \"must be used inside \u003CTabs>\" error rather than\n",[33,5069,5070],{"class":35,"line":132},[33,5071,5072],{"class":39},"\u002F\u002F letting a cryptic \"cannot read property of null\" surface.\n",[33,5074,5075],{"class":35,"line":138},[33,5076,65],{"emptyLinePlaceholder":64},[33,5078,5079],{"class":35,"line":144},[33,5080,5081],{"class":39},"\u002F\u002F [Performance] Render props create a new function every render — wrap the\n",[33,5083,5084],{"class":35,"line":333},[33,5085,5086],{"class":39},"\u002F\u002F passed function in useCallback if it's passed to a memoized child, or the\n",[33,5088,5089],{"class":35,"line":339},[33,5090,5091],{"class":39},"\u002F\u002F child re-renders every time even if its inputs haven't changed.\n",[33,5093,5094],{"class":35,"line":523},[33,5095,65],{"emptyLinePlaceholder":64},[33,5097,5098],{"class":35,"line":540},[33,5099,5100],{"class":39},"\u002F\u002F [Idiom] Prefer custom hooks over HOCs for sharing cross-cutting LOGIC in new\n",[33,5102,5103],{"class":35,"line":555},[33,5104,5105],{"class":39},"\u002F\u002F code. HOCs remain relevant mainly in older codebases and prop-injection\n",[33,5107,5108],{"class":35,"line":561},[33,5109,5110],{"class":39},"\u002F\u002F library patterns that predate hooks.\n",[33,5112,5113],{"class":35,"line":566},[33,5114,65],{"emptyLinePlaceholder":64},[33,5116,5117],{"class":35,"line":1162},[33,5118,5119],{"class":39},"\u002F\u002F [Idiom] When using `children` as a render prop, name the prop explicitly\n",[33,5121,5122],{"class":35,"line":1167},[33,5123,5124],{"class":39},"\u002F\u002F (`render` or `children`) and document that it receives a function — a caller\n",[33,5126,5127],{"class":35,"line":1500},[33,5128,5129],{"class":39},"\u002F\u002F passing JSX instead of a function will see \"children is not a function\" with\n",[33,5131,5132],{"class":35,"line":1517},[33,5133,5134],{"class":39},"\u002F\u002F no obvious explanation.\n",[14,5136,5138],{"id":5137},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[19,5140,5142],{"filename":5141,"language":22},"edge_cases.js",[24,5143,5145],{"className":26,"code":5144,"language":22,"meta":28,"style":28},"\u002F\u002F [Gotcha] Compound components silently break if their shared Context has no\n\u002F\u002F default value and a sub-component is rendered outside the parent — useContext\n\u002F\u002F returns null\u002Fundefined rather than throwing, so the failure surfaces later as\n\u002F\u002F a confusing \"cannot read properties of null\" deep inside the sub-component.\n\n\u002F\u002F [Gotcha] Static property assignment (Tabs.Tab = Tab) is purely cosmetic and\n\u002F\u002F easy to over-trust — nothing prevents importing and using Tab directly without\n\u002F\u002F the Tabs. prefix, and nothing about the assignment enforces that Tab is ever\n\u002F\u002F rendered inside a \u003CTabs> provider. The actual coupling is the shared Context.\n\n\u002F\u002F [Gotcha] HOCs that don't forward ref break any consumer trying to get a DOM\n\u002F\u002F ref through the wrapper — a plain `function Enhanced(props) { return \u003CWrapped\n\u002F\u002F {...props} \u002F> }` silently drops the ref. Must use React.forwardRef explicitly.\n\n\u002F\u002F [Gotcha] Creating an HOC inside render (const Enhanced = withAuth(Comp)) produces\n\u002F\u002F a NEW component type every render → React unmounts and remounts the entire\n\u002F\u002F subtree → state is destroyed, effects re-run. Always apply HOCs at module scope.\n\n\u002F\u002F [Gotcha] Stacking multiple HOCs (withAuth(withLogging(withTheme(Comp)))) obscures\n\u002F\u002F the actual prop flow and makes DevTools' tree hard to read — each HOC adds a\n\u002F\u002F wrapper with a generic name. This readability cost is part of why hooks are\n\u002F\u002F now generally preferred for new code.\n\n\u002F\u002F [Gotcha] Render props passed as inline functions ({({ x }) => ...}) create a\n\u002F\u002F new function reference every render. If the component receiving the render prop\n\u002F\u002F is memoized, it will still re-render every time because the function prop changed.\n\n\u002F\u002F [Gotcha] A \"rigid\" data-driven component (items array + internal render) loses\n\u002F\u002F the ability to interleave arbitrary non-item markup between its rendered pieces\n\u002F\u002F — a compound component doesn't have this limitation, since the caller writes\n\u002F\u002F the actual JSX structure. Choosing the rigid form is a real trade against\n\u002F\u002F flexibility, not merely a stylistic preference.\n\n\u002F\u002F [Gotcha] cloneElement-based compound components (the React.Children pattern)\n\u002F\u002F don't work across abstraction boundaries — if a consumer wraps a child in a\n\u002F\u002F custom component, cloneElement injects props into the WRAPPER, not the actual\n\u002F\u002F target component, and the injected props never reach the intended receiver.\n",[30,5146,5147,5152,5157,5162,5167,5171,5176,5181,5186,5191,5195,5200,5205,5210,5214,5219,5224,5229,5233,5238,5243,5248,5253,5257,5262,5267,5272,5276,5281,5286,5291,5296,5301,5305,5310,5315,5320],{"__ignoreMap":28},[33,5148,5149],{"class":35,"line":36},[33,5150,5151],{"class":39},"\u002F\u002F [Gotcha] Compound components silently break if their shared Context has no\n",[33,5153,5154],{"class":35,"line":43},[33,5155,5156],{"class":39},"\u002F\u002F default value and a sub-component is rendered outside the parent — useContext\n",[33,5158,5159],{"class":35,"line":49},[33,5160,5161],{"class":39},"\u002F\u002F returns null\u002Fundefined rather than throwing, so the failure surfaces later as\n",[33,5163,5164],{"class":35,"line":55},[33,5165,5166],{"class":39},"\u002F\u002F a confusing \"cannot read properties of null\" deep inside the sub-component.\n",[33,5168,5169],{"class":35,"line":61},[33,5170,65],{"emptyLinePlaceholder":64},[33,5172,5173],{"class":35,"line":68},[33,5174,5175],{"class":39},"\u002F\u002F [Gotcha] Static property assignment (Tabs.Tab = Tab) is purely cosmetic and\n",[33,5177,5178],{"class":35,"line":74},[33,5179,5180],{"class":39},"\u002F\u002F easy to over-trust — nothing prevents importing and using Tab directly without\n",[33,5182,5183],{"class":35,"line":80},[33,5184,5185],{"class":39},"\u002F\u002F the Tabs. prefix, and nothing about the assignment enforces that Tab is ever\n",[33,5187,5188],{"class":35,"line":86},[33,5189,5190],{"class":39},"\u002F\u002F rendered inside a \u003CTabs> provider. The actual coupling is the shared Context.\n",[33,5192,5193],{"class":35,"line":92},[33,5194,65],{"emptyLinePlaceholder":64},[33,5196,5197],{"class":35,"line":97},[33,5198,5199],{"class":39},"\u002F\u002F [Gotcha] HOCs that don't forward ref break any consumer trying to get a DOM\n",[33,5201,5202],{"class":35,"line":103},[33,5203,5204],{"class":39},"\u002F\u002F ref through the wrapper — a plain `function Enhanced(props) { return \u003CWrapped\n",[33,5206,5207],{"class":35,"line":109},[33,5208,5209],{"class":39},"\u002F\u002F {...props} \u002F> }` silently drops the ref. Must use React.forwardRef explicitly.\n",[33,5211,5212],{"class":35,"line":115},[33,5213,65],{"emptyLinePlaceholder":64},[33,5215,5216],{"class":35,"line":121},[33,5217,5218],{"class":39},"\u002F\u002F [Gotcha] Creating an HOC inside render (const Enhanced = withAuth(Comp)) produces\n",[33,5220,5221],{"class":35,"line":127},[33,5222,5223],{"class":39},"\u002F\u002F a NEW component type every render → React unmounts and remounts the entire\n",[33,5225,5226],{"class":35,"line":132},[33,5227,5228],{"class":39},"\u002F\u002F subtree → state is destroyed, effects re-run. Always apply HOCs at module scope.\n",[33,5230,5231],{"class":35,"line":138},[33,5232,65],{"emptyLinePlaceholder":64},[33,5234,5235],{"class":35,"line":144},[33,5236,5237],{"class":39},"\u002F\u002F [Gotcha] Stacking multiple HOCs (withAuth(withLogging(withTheme(Comp)))) obscures\n",[33,5239,5240],{"class":35,"line":333},[33,5241,5242],{"class":39},"\u002F\u002F the actual prop flow and makes DevTools' tree hard to read — each HOC adds a\n",[33,5244,5245],{"class":35,"line":339},[33,5246,5247],{"class":39},"\u002F\u002F wrapper with a generic name. This readability cost is part of why hooks are\n",[33,5249,5250],{"class":35,"line":523},[33,5251,5252],{"class":39},"\u002F\u002F now generally preferred for new code.\n",[33,5254,5255],{"class":35,"line":540},[33,5256,65],{"emptyLinePlaceholder":64},[33,5258,5259],{"class":35,"line":555},[33,5260,5261],{"class":39},"\u002F\u002F [Gotcha] Render props passed as inline functions ({({ x }) => ...}) create a\n",[33,5263,5264],{"class":35,"line":561},[33,5265,5266],{"class":39},"\u002F\u002F new function reference every render. If the component receiving the render prop\n",[33,5268,5269],{"class":35,"line":566},[33,5270,5271],{"class":39},"\u002F\u002F is memoized, it will still re-render every time because the function prop changed.\n",[33,5273,5274],{"class":35,"line":1162},[33,5275,65],{"emptyLinePlaceholder":64},[33,5277,5278],{"class":35,"line":1167},[33,5279,5280],{"class":39},"\u002F\u002F [Gotcha] A \"rigid\" data-driven component (items array + internal render) loses\n",[33,5282,5283],{"class":35,"line":1500},[33,5284,5285],{"class":39},"\u002F\u002F the ability to interleave arbitrary non-item markup between its rendered pieces\n",[33,5287,5288],{"class":35,"line":1517},[33,5289,5290],{"class":39},"\u002F\u002F — a compound component doesn't have this limitation, since the caller writes\n",[33,5292,5293],{"class":35,"line":1546},[33,5294,5295],{"class":39},"\u002F\u002F the actual JSX structure. Choosing the rigid form is a real trade against\n",[33,5297,5298],{"class":35,"line":1566},[33,5299,5300],{"class":39},"\u002F\u002F flexibility, not merely a stylistic preference.\n",[33,5302,5303],{"class":35,"line":1572},[33,5304,65],{"emptyLinePlaceholder":64},[33,5306,5307],{"class":35,"line":1578},[33,5308,5309],{"class":39},"\u002F\u002F [Gotcha] cloneElement-based compound components (the React.Children pattern)\n",[33,5311,5312],{"class":35,"line":1587},[33,5313,5314],{"class":39},"\u002F\u002F don't work across abstraction boundaries — if a consumer wraps a child in a\n",[33,5316,5317],{"class":35,"line":1592},[33,5318,5319],{"class":39},"\u002F\u002F custom component, cloneElement injects props into the WRAPPER, not the actual\n",[33,5321,5322],{"class":35,"line":1597},[33,5323,5324],{"class":39},"\u002F\u002F target component, and the injected props never reach the intended receiver.\n",[14,5326,5328],{"id":5327},"spot-the-bug","🧠 Spot the Bug",[314,5330,5331,5332,5335,5336,5339,5340,5343],{},"A design system's ",[30,5333,5334],{},"Modal"," compound component works everywhere — except on one page, where ",[30,5337,5338],{},"Modal.CloseButton"," throws ",[30,5341,5342],{},"Cannot read properties of undefined (reading 'close')"," as soon as the modal opens:",[19,5345,5347],{"filename":5346,"language":22},"spot_the_bug.js",[24,5348,5350],{"className":26,"code":5349,"language":22,"meta":28,"style":28},"const ModalContext = createContext()\n\nfunction Modal({ children, onClose }) {\n  return (\n    \u003CModalContext.Provider value={{ close: onClose }}>\n      \u003Cdiv className=\"modal-overlay\">\n        \u003Cdiv className=\"modal\">{children}\u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n    \u003C\u002FModalContext.Provider>\n  )\n}\n\nfunction CloseButton() {\n  const { close } = useContext(ModalContext)\n  return \u003Cbutton onClick={close}>×\u003C\u002Fbutton>\n}\n\nModal.CloseButton = CloseButton\n\nfunction ReportModal() {\n  return (\n    \u003C>\n      \u003CModal.CloseButton \u002F>\n      \u003CModal onClose={() => setOpen(false)}>\n        \u003CReportForm \u002F>\n      \u003C\u002FModal>\n    \u003C\u002F>\n  )\n}\n",[30,5351,5352,5365,5369,5387,5393,5407,5422,5441,5449,5457,5461,5465,5469,5478,5496,5515,5519,5523,5533,5537,5546,5552,5556,5564,5590,5599,5607,5611,5615],{"__ignoreMap":28},[33,5353,5354,5356,5359,5361,5363],{"class":35,"line":36},[33,5355,1228],{"class":187},[33,5357,5358],{"class":290}," ModalContext",[33,5360,936],{"class":187},[33,5362,1236],{"class":191},[33,5364,3647],{"class":195},[33,5366,5367],{"class":35,"line":43},[33,5368,65],{"emptyLinePlaceholder":64},[33,5370,5371,5373,5376,5378,5380,5382,5385],{"class":35,"line":49},[33,5372,188],{"class":187},[33,5374,5375],{"class":191}," Modal",[33,5377,196],{"class":195},[33,5379,154],{"class":199},[33,5381,387],{"class":195},[33,5383,5384],{"class":199},"onClose",[33,5386,202],{"class":195},[33,5388,5389,5391],{"class":35,"line":55},[33,5390,207],{"class":187},[33,5392,282],{"class":195},[33,5394,5395,5397,5400,5402,5404],{"class":35,"line":61},[33,5396,287],{"class":195},[33,5398,5399],{"class":290},"ModalContext.Provider",[33,5401,1308],{"class":191},[33,5403,220],{"class":187},[33,5405,5406],{"class":195},"{{ close: onClose }}>\n",[33,5408,5409,5411,5413,5415,5417,5420],{"class":35,"line":68},[33,5410,298],{"class":195},[33,5412,214],{"class":213},[33,5414,217],{"class":191},[33,5416,220],{"class":187},[33,5418,5419],{"class":223},"\"modal-overlay\"",[33,5421,232],{"class":195},[33,5423,5424,5426,5428,5430,5432,5435,5437,5439],{"class":35,"line":74},[33,5425,1825],{"class":195},[33,5427,214],{"class":213},[33,5429,217],{"class":191},[33,5431,220],{"class":187},[33,5433,5434],{"class":223},"\"modal\"",[33,5436,227],{"class":195},[33,5438,214],{"class":213},[33,5440,232],{"class":195},[33,5442,5443,5445,5447],{"class":35,"line":80},[33,5444,1869],{"class":195},[33,5446,214],{"class":213},[33,5448,232],{"class":195},[33,5450,5451,5453,5455],{"class":35,"line":86},[33,5452,326],{"class":195},[33,5454,5399],{"class":290},[33,5456,232],{"class":195},[33,5458,5459],{"class":35,"line":92},[33,5460,336],{"class":195},[33,5462,5463],{"class":35,"line":97},[33,5464,237],{"class":195},[33,5466,5467],{"class":35,"line":103},[33,5468,65],{"emptyLinePlaceholder":64},[33,5470,5471,5473,5476],{"class":35,"line":109},[33,5472,188],{"class":187},[33,5474,5475],{"class":191}," CloseButton",[33,5477,275],{"class":195},[33,5479,5480,5482,5484,5487,5489,5491,5493],{"class":35,"line":115},[33,5481,968],{"class":187},[33,5483,1427],{"class":195},[33,5485,5486],{"class":290},"close",[33,5488,1436],{"class":195},[33,5490,220],{"class":187},[33,5492,1441],{"class":191},[33,5494,5495],{"class":195},"(ModalContext)\n",[33,5497,5498,5500,5502,5504,5506,5508,5511,5513],{"class":35,"line":121},[33,5499,207],{"class":187},[33,5501,210],{"class":195},[33,5503,994],{"class":213},[33,5505,1145],{"class":191},[33,5507,220],{"class":187},[33,5509,5510],{"class":195},"{close}>×\u003C\u002F",[33,5512,994],{"class":213},[33,5514,232],{"class":195},[33,5516,5517],{"class":35,"line":127},[33,5518,237],{"class":195},[33,5520,5521],{"class":35,"line":132},[33,5522,65],{"emptyLinePlaceholder":64},[33,5524,5525,5528,5530],{"class":35,"line":138},[33,5526,5527],{"class":195},"Modal.CloseButton ",[33,5529,220],{"class":187},[33,5531,5532],{"class":195}," CloseButton\n",[33,5534,5535],{"class":35,"line":144},[33,5536,65],{"emptyLinePlaceholder":64},[33,5538,5539,5541,5544],{"class":35,"line":333},[33,5540,188],{"class":187},[33,5542,5543],{"class":191}," ReportModal",[33,5545,275],{"class":195},[33,5547,5548,5550],{"class":35,"line":339},[33,5549,207],{"class":187},[33,5551,282],{"class":195},[33,5553,5554],{"class":35,"line":523},[33,5555,1114],{"class":195},[33,5557,5558,5560,5562],{"class":35,"line":540},[33,5559,298],{"class":195},[33,5561,5338],{"class":290},[33,5563,3241],{"class":195},[33,5565,5566,5568,5570,5573,5575,5577,5579,5582,5584,5587],{"class":35,"line":555},[33,5567,298],{"class":195},[33,5569,5334],{"class":290},[33,5571,5572],{"class":191}," onClose",[33,5574,220],{"class":187},[33,5576,1554],{"class":195},[33,5578,1557],{"class":187},[33,5580,5581],{"class":191}," setOpen",[33,5583,1057],{"class":195},[33,5585,5586],{"class":290},"false",[33,5588,5589],{"class":195},")}>\n",[33,5591,5592,5594,5597],{"class":35,"line":561},[33,5593,1825],{"class":195},[33,5595,5596],{"class":290},"ReportForm",[33,5598,3241],{"class":195},[33,5600,5601,5603,5605],{"class":35,"line":566},[33,5602,1869],{"class":195},[33,5604,5334],{"class":290},[33,5606,232],{"class":195},[33,5608,5609],{"class":35,"line":1162},[33,5610,1159],{"class":195},[33,5612,5613],{"class":35,"line":1167},[33,5614,336],{"class":195},[33,5616,5617],{"class":35,"line":1500},[33,5618,237],{"class":195},[5620,5621,5622,5626,5669,5689,5762],"details",{},[5623,5624,5625],"summary",{},"Answer",[314,5627,5628,5630,5631,5635,5636,5639,5640,5643,5644,5646,5647,5649,5650,5653,5654,5657,5658,5661,5662,5665,5666,5668],{},[30,5629,5338],{}," is rendered as a ",[5632,5633,5634],"strong",{},"sibling before"," ",[30,5637,5638],{},"\u003CModal>"," in ",[30,5641,5642],{},"ReportModal",", entirely outside the ",[30,5645,5399],{}," that ",[30,5648,5334],{}," creates internally. ",[30,5651,5652],{},"useContext(ModalContext)"," at that position returns the context's default value — ",[30,5655,5656],{},"undefined",", since ",[30,5659,5660],{},"createContext()"," was called with no argument — so destructuring ",[30,5663,5664],{},"{ close }"," from ",[30,5667,5656],{}," throws immediately.",[314,5670,5671,5674,5675,5635,5678,5635,5682,5684,5685,5688],{},[5632,5672,5673],{},"Fix",": place ",[30,5676,5677],{},"CloseButton",[5679,5680,5681],"em",{},"inside",[30,5683,5638],{},", and give ",[30,5686,5687],{},"createContext"," a meaningful default (or add a null-check that throws a clear error):",[24,5690,5692],{"className":26,"code":5691,"language":22,"meta":28,"style":28},"function ReportModal() {\n  return (\n    \u003CModal onClose={() => setOpen(false)}>\n      \u003CModal.CloseButton \u002F>\n      \u003CReportForm \u002F>\n    \u003C\u002FModal>\n  )\n}\n",[30,5693,5694,5702,5708,5730,5738,5746,5754,5758],{"__ignoreMap":28},[33,5695,5696,5698,5700],{"class":35,"line":36},[33,5697,188],{"class":187},[33,5699,5543],{"class":191},[33,5701,275],{"class":195},[33,5703,5704,5706],{"class":35,"line":43},[33,5705,207],{"class":187},[33,5707,282],{"class":195},[33,5709,5710,5712,5714,5716,5718,5720,5722,5724,5726,5728],{"class":35,"line":49},[33,5711,287],{"class":195},[33,5713,5334],{"class":290},[33,5715,5572],{"class":191},[33,5717,220],{"class":187},[33,5719,1554],{"class":195},[33,5721,1557],{"class":187},[33,5723,5581],{"class":191},[33,5725,1057],{"class":195},[33,5727,5586],{"class":290},[33,5729,5589],{"class":195},[33,5731,5732,5734,5736],{"class":35,"line":55},[33,5733,298],{"class":195},[33,5735,5338],{"class":290},[33,5737,3241],{"class":195},[33,5739,5740,5742,5744],{"class":35,"line":61},[33,5741,298],{"class":195},[33,5743,5596],{"class":290},[33,5745,3241],{"class":195},[33,5747,5748,5750,5752],{"class":35,"line":68},[33,5749,326],{"class":195},[33,5751,5334],{"class":290},[33,5753,232],{"class":195},[33,5755,5756],{"class":35,"line":74},[33,5757,336],{"class":195},[33,5759,5760],{"class":35,"line":80},[33,5761,237],{"class":195},[314,5763,5764,5765,5768,5769,5772,5773,5775,5776,5778],{},"The lesson: a compound component's sub-components only receive shared state from an ancestor ",[30,5766,5767],{},"Provider"," actually rendered ",[5632,5770,5771],{},"above"," them — placing ",[30,5774,5338],{}," as a sibling rather than a child of ",[30,5777,5334],{}," breaks the implicit contract compound components rely on.",[14,5780,5782],{"id":5781},"key-takeaways","Key Takeaways",[19,5784,5786],{"filename":5785,"language":22},"key_takeaways.js",[24,5787,5789],{"className":26,"code":5788,"language":22,"meta":28,"style":28},"\u002F\u002F 1. React deliberately has NO component-level inheritance — composition (children,\n\u002F\u002F    slot props, prop forwarding) covers every case a class hierarchy would, with\n\u002F\u002F    shallower, more explicit coupling.\n\n\u002F\u002F 2. `children` is the default for single-slot wrappers; named props generalize\n\u002F\u002F    it into multiple independent slots when a layout needs more than one region.\n\n\u002F\u002F 3. \"Specialized\" variants = thin wrapper functions forwarding `...props` and\n\u002F\u002F    overriding only the differing prop. This automatically inherits any future\n\u002F\u002F    capability the base component gains, mirroring OO inheritance without coupling.\n\n\u002F\u002F 4. Compound components share implicit state among related sub-components via\n\u002F\u002F    Context, letting the caller control arrangement and interleaved markup.\n\u002F\u002F    Choose this over a rigid data-prop component when that flexibility matters.\n\n\u002F\u002F 5. Render props invert control: the component owns state, the caller owns\n\u002F\u002F    rendering. Still useful when the shared logic is about WHAT to render, but\n\u002F\u002F    largely superseded by hooks for pure stateful logic sharing.\n\n\u002F\u002F 6. HOCs are the legacy enhancement mechanism — still in older codebases and\n\u002F\u002F    some libraries, but hooks + early returns replace them for new code: no\n\u002F\u002F    wrapper layer, no ref forwarding issues, no static method hoisting.\n\n\u002F\u002F 7. Static property assignment (Tabs.Tab = Tab) is naming convenience only —\n\u002F\u002F    the real coupling is the shared Context. Sub-components rendered outside\n\u002F\u002F    their parent's Provider read null\u002Fundefined unless explicitly guarded.\n",[30,5790,5791,5796,5801,5806,5810,5815,5820,5824,5829,5834,5839,5843,5848,5853,5858,5862,5867,5872,5877,5881,5886,5891,5896,5900,5905,5910],{"__ignoreMap":28},[33,5792,5793],{"class":35,"line":36},[33,5794,5795],{"class":39},"\u002F\u002F 1. React deliberately has NO component-level inheritance — composition (children,\n",[33,5797,5798],{"class":35,"line":43},[33,5799,5800],{"class":39},"\u002F\u002F    slot props, prop forwarding) covers every case a class hierarchy would, with\n",[33,5802,5803],{"class":35,"line":49},[33,5804,5805],{"class":39},"\u002F\u002F    shallower, more explicit coupling.\n",[33,5807,5808],{"class":35,"line":55},[33,5809,65],{"emptyLinePlaceholder":64},[33,5811,5812],{"class":35,"line":61},[33,5813,5814],{"class":39},"\u002F\u002F 2. `children` is the default for single-slot wrappers; named props generalize\n",[33,5816,5817],{"class":35,"line":68},[33,5818,5819],{"class":39},"\u002F\u002F    it into multiple independent slots when a layout needs more than one region.\n",[33,5821,5822],{"class":35,"line":74},[33,5823,65],{"emptyLinePlaceholder":64},[33,5825,5826],{"class":35,"line":80},[33,5827,5828],{"class":39},"\u002F\u002F 3. \"Specialized\" variants = thin wrapper functions forwarding `...props` and\n",[33,5830,5831],{"class":35,"line":86},[33,5832,5833],{"class":39},"\u002F\u002F    overriding only the differing prop. This automatically inherits any future\n",[33,5835,5836],{"class":35,"line":92},[33,5837,5838],{"class":39},"\u002F\u002F    capability the base component gains, mirroring OO inheritance without coupling.\n",[33,5840,5841],{"class":35,"line":97},[33,5842,65],{"emptyLinePlaceholder":64},[33,5844,5845],{"class":35,"line":103},[33,5846,5847],{"class":39},"\u002F\u002F 4. Compound components share implicit state among related sub-components via\n",[33,5849,5850],{"class":35,"line":109},[33,5851,5852],{"class":39},"\u002F\u002F    Context, letting the caller control arrangement and interleaved markup.\n",[33,5854,5855],{"class":35,"line":115},[33,5856,5857],{"class":39},"\u002F\u002F    Choose this over a rigid data-prop component when that flexibility matters.\n",[33,5859,5860],{"class":35,"line":121},[33,5861,65],{"emptyLinePlaceholder":64},[33,5863,5864],{"class":35,"line":127},[33,5865,5866],{"class":39},"\u002F\u002F 5. Render props invert control: the component owns state, the caller owns\n",[33,5868,5869],{"class":35,"line":132},[33,5870,5871],{"class":39},"\u002F\u002F    rendering. Still useful when the shared logic is about WHAT to render, but\n",[33,5873,5874],{"class":35,"line":138},[33,5875,5876],{"class":39},"\u002F\u002F    largely superseded by hooks for pure stateful logic sharing.\n",[33,5878,5879],{"class":35,"line":144},[33,5880,65],{"emptyLinePlaceholder":64},[33,5882,5883],{"class":35,"line":333},[33,5884,5885],{"class":39},"\u002F\u002F 6. HOCs are the legacy enhancement mechanism — still in older codebases and\n",[33,5887,5888],{"class":35,"line":339},[33,5889,5890],{"class":39},"\u002F\u002F    some libraries, but hooks + early returns replace them for new code: no\n",[33,5892,5893],{"class":35,"line":523},[33,5894,5895],{"class":39},"\u002F\u002F    wrapper layer, no ref forwarding issues, no static method hoisting.\n",[33,5897,5898],{"class":35,"line":540},[33,5899,65],{"emptyLinePlaceholder":64},[33,5901,5902],{"class":35,"line":555},[33,5903,5904],{"class":39},"\u002F\u002F 7. Static property assignment (Tabs.Tab = Tab) is naming convenience only —\n",[33,5906,5907],{"class":35,"line":561},[33,5908,5909],{"class":39},"\u002F\u002F    the real coupling is the shared Context. Sub-components rendered outside\n",[33,5911,5912],{"class":35,"line":566},[33,5913,5914],{"class":39},"\u002F\u002F    their parent's Provider read null\u002Fundefined unless explicitly guarded.\n",[5916,5917,5918],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}html pre.shiki code .sk71V, html code.shiki .sk71V{--shiki-default:#22863A;--shiki-github-dark:#85E89D}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}",{"title":28,"searchDepth":43,"depth":43,"links":5920},[5921,5922,5924,5927,5928,5933,5937,5940,5941,5942,5943,5944,5945],{"id":16,"depth":43,"text":17},{"id":150,"depth":43,"text":5923},"The children Prop: Single-Slot Composition",{"id":344,"depth":43,"text":345,"children":5925},[5926],{"id":572,"depth":49,"text":573},{"id":888,"depth":43,"text":889},{"id":1172,"depth":43,"text":1173,"children":5929},[5930,5931],{"id":1946,"depth":49,"text":1947},{"id":2232,"depth":49,"text":5932},"Compound Component with Dynamic Children via React.Children",{"id":2629,"depth":43,"text":2630,"children":5934},[5935],{"id":3331,"depth":49,"text":5936},"Render Prop via children (the idiomatic form)",{"id":3565,"depth":43,"text":3566,"children":5938},[5939],{"id":3749,"depth":49,"text":3750},{"id":4215,"depth":43,"text":4216},{"id":4477,"depth":43,"text":4478},{"id":4981,"depth":43,"text":4982},{"id":5137,"depth":43,"text":5138},{"id":5327,"depth":43,"text":5328},{"id":5781,"depth":43,"text":5782},"Why React has no component inheritance, children prop patterns, named slots, compound components with Context, render props, HOCs vs hooks as the modern composition mechanism, and specialization via configuration. Code-first reference for mid-to-senior React engineers.","md",{},"\u002Freact\u002F15-composition-vs-inheritance",{"title":5,"description":5946},"react\u002F15-composition-vs-inheritance","tD09sbpq-uNQEnOa5S1GGiUGa0VZF7k53Au9qCVtado",1789924652286]