11.13-bob: React pattern'lar — HOC, render props, compound components
11-QISM — Frontend: React · 13-mavzu
1. Kirish va motivatsiya
11.7-bobda custom hooklar bilan mantiqni qayta ishlatishni o'rgandik — bu zamonaviy React'ning asosiy usuli. Lekin React 7+ yil tarixga ega va hooklardan oldin ham mantiq va UI'ni qayta ishlatish kerak edi. O'sha davrda ikkita klassik naqsh ishlatilardi: HOC (Higher-Order Component) va render props. Bu naqshlarni bilish ikki sababga ko'ra muhim: (1) ko'p mavjud kod va kutubxona (eski va yangi) ularni ishlatadi — siz ularni o'qiy va tushuna olishingiz kerak; (2) ba'zi holatlarda (ayniqsa compound components) ular hali ham eng yaxshi yechim. Bundan tashqari, bu naqshlarni tushunish — React'ning chuqur falsafasini (kompozitsiya, mas'uliyat ajratish) anglashga yordam beradi.
Bu bobda uchta pattern'ni ko'ramiz, har biri mantiq yoki UI'ni qayta ishlatishning turli usuli. HOC — komponentni o'rovchi funksiya (withAuth(Component) — komponentga qo'shimcha imkoniyat "qo'shadi"). Render props — komponent o'z mantig'ini funksiya prop orqali ulashadi (<Mouse>{pos => ...}</Mouse>). Va eng muhimi, hali ham keng ishlatiladigan — compound components — bir-biriga bog'liq komponentlar oilasi (<Tabs>, <Tabs.Tab>, <Tabs.Panel> — birgalikda ishlaydi, holatni Context orqali ulashadi). Bu naqshlarni custom hooklar 11.7-bob bilan solishtiramiz — qachon qaysi biri afzal — va UI kutubxonalar (Radix, MUI) qanday qurilganini tushunamiz.
Bu bob: nega pattern'lar (mantiq/UI qayta ishlatish — custom hook bilan taqqos), HOC (nima, qanday yoziladi/ishlatiladi, muammolari — wrapper hell, props to'qnashuvi — nega hook afzal), render props (nima, qanday, children as function, hooklar bilan taqqos), compound components (nima — Tab/Accordion/Select, Context bilan, control props/state reducer — ilg'or, prop getters), va qachon qaysi pattern (hook default, compound UI uchun). Bu naqshlarning har birini to'liq va amaliy holatda ochamiz.
O'xshatish: Bu pattern'lar — bir narsani o'rashning uch xil usuli (sovg'a qadoqlash). HOC — bu tashqi quti: sovg'ani (komponentni) qutiga solasiz, qutiga qo'shimcha narsalar (yorliq, lenta — qo'shimcha props) qo'shasiz. Lekin ko'p quti ichma-ich bo'lsa — ochish qiyin (wrapper hell). Render props — bu "o'zingiz qadoqla" xizmati: do'kon sizga materiallar (mantiq) beradi, qanday qadoqlashni (UI) siz funksiya orqali aytasiz — moslashuvchan, lekin har safar yozasiz. Custom hook 11.7-bob — bu zamonaviy yetkazib berish: materiallar to'g'ridan keladi (funksiya chaqiruvi —
useMouse()), ortiqcha quti yo'q (eng toza). Compound components — bu mebel to'plami: stol (<Tabs>) va stullar (<Tab>) alohida keladi, lekin birga ishlashga mo'ljallangan (bir uslubda, bir-birini "biladi") — foydalanuvchi ularni erkin joylashtiradi.
Nega muhim?
- Mavjud kodni o'qiy olish — ko'p kutubxona/eski kod HOC/render props ishlatadi (tushunmasangiz, ishlay olmaysiz).
- Compound components — Tab, Accordion, Modal, Select kabi murakkab UI'ning eng yaxshi naqshi (hali keng).
- Kutubxona dizayni — Radix, MUI, Headless UI shu naqshlarga qurilgan (qanday ishlashini tushunasiz).
- Chuqur React — kompozitsiya, mas'uliyat ajratish, moslashuvchan API — senior dizayn tafakkuri.
2. Nazariya — chuqur tushuntirish
2.1. Nega pattern'lar — mantiq/UI qayta ishlatish
MUAMMO: bir nechta komponent BIR XIL mantiq yoki UI tuzilishini ishlatadi
uni qanday QAYTA ISHLATISH (DRY — 9.1)?
3 YONDASHUV (tarixiy tartibda):
1. HOC (eski) — komponentni o'rovchi funksiya (withX(Component))
2. RENDER PROPS (eski) — funksiya prop orqali mantiq ulashish
3. CUSTOM HOOK (zamonaviy — 11.7) — mantiqni funksiya sifatida (useX())
MANTIQ vs UI qayta ishlatish:
- MANTIQ (state, effect, data) custom hook 11.7-bob afzal (zamonaviy, toza)
- UI TUZILISHI / KOMPOZITSIYA compound components 2.9-bob yoki render props
┌────────────────────────────────────────────────────────────┐
│ Mantiq ulashish: HOC/render props ENDI custom hook 11.7-bob │
│ UI tuzilishi ulashish: compound components (hook qila olmaydi) │
└────────────────────────────────────────────────────────────┘
Hooklar HOC/render props'ning KO'PINI almashtirdi (mantiq uchun)
LEKIN compound components — hali ham UI naqshi uchun eng yaxshi (hook emas)Nega pattern'lar — bir nechta komponent bir xil mantiq yoki UI tuzilishini ishlatganda, uni qanday qayta ishlatish (DRY — 9.1) muammosini hal qiladi. Tarixiy tartibda uch yondashuv: HOC (eski — komponentni o'rovchi funksiya,
withX(Component)), render props (eski — funksiya prop orqali mantiq ulashish), custom hook (zamonaviy — 11.7 — mantiqni funksiya sifatida,useX()). Muhim ajratish — nimani qayta ishlatish: (1) mantiq (state, effect, data fetching, obuna) custom hook afzal (zamonaviy, toza, "wrapper hell"siz — 11.7: 2.1); (2) UI tuzilishi/kompozitsiya (bir-biriga bog'liq komponentlar oilasi) compound components 2.9-bob yoki render props — bularni hook qila olmaydi. Ikki muhim xulosa: (1) hooklar HOC/render props'ning ko'pini almashtirdi — mantiq ulashish uchun endi custom hook ishlatiladi (HOC/render props eski kodda qoladi); (2) lekin compound components — UI komponentlari o'rtasida holat va kontekstni ulashish naqshi sifatida hali ham eng yaxshi (hook bu vazifani bajara olmaydi). Demak pattern'lar eskirmagan — har biri o'z o'rnida. Bu bob ularni qachon va qanday ishlatishni o'rgatadi.
2.1b. Komponent kompozitsiya — children, slot, komponent prop
KOMPOZITSIYA — komponentni "ichiga qo'yish" (React'ning eng tabiiy qayta ishlatish usuli):
1. children — eng oddiy: komponent ichidagi hamma narsa `props.children`:
function Card({ children }) { return <div className="card">{children}</div>; }
<Card><h2>Sarlavha</h2><p>Matn</p></Card> // children = <h2/> + <p/>
2. SLOT pattern — bir nechta "teshik" (nomlangan children):
function Layout({ header, sidebar, children }) {
return (<div><header>{header}</header><aside>{sidebar}</aside><main>{children}</main></div>);
}
<Layout header={<Nav/>} sidebar={<Menu/>}><Content/></Layout> // JSX prop = slot
3. KOMPONENT PROP — komponentni prop sifatida uzatish (turini boshqarish):
function List({ items, ItemComponent }) {
return items.map(x => <ItemComponent key={x.id} item={x} />); // qaysi komponent — prop
}
<List items={users} ItemComponent={UserCard} /> // <List items={products} ItemComponent={ProductCard}/>
Kompozitsiya — "prop drilling"/HOC o'rniga ko'p muammoni hal qiladi (JSX'ni prop qil)
children/slot — Context'siz, HOC'siz UI qayta ishlatishning eng toza usuliKomponent kompozitsiya — HOC/render props'dan oldin o'ylab ko'rish kerak bo'lgan eng tabiiy usul. React'da qayta ishlatishning asosi — komponentni boshqa komponent ichiga qo'yish (kompozitsiya), meros (inheritance) emas. Uch shakl: (1) children — eng oddiy:
function Card({ children }) { return <div className="card">{children}</div> }—<Card>ichidagi hamma narsaprops.childrenbo'lib keladi (Carduni qayerga qo'yishni hal qiladi). Bu — "o'rash"ni HOC'siz beradi. (2) slot pattern — bir nechta "teshik": komponent bir nechta JSX'ni alohida prop sifatida oladi (<Layout header={<Nav/>} sidebar={<Menu/>}>...</Layout>) — har bir prop bir "slot"ga joylashadi (Vue'dagi slot yoki HTML<slot>ga o'xshash g'oya). (3) komponent prop — komponentning o'zini prop sifatida uzatish:<List items={users} ItemComponent={UserCard} />—Listumumiy mantiqni (ro'yxatlash) bajaradi, lekin har bir elementni qaysi komponent ko'rsatishini chaqiruvchi hal qiladi. Ikki muhim nuqta: (1) kompozitsiya ko'p holatlarda HOC yoki prop drilling'ni umuman keraksiz qiladi — masalan "layout"ni qayta ishlatish uchun HOC yozish shart emas,children/slot yetadi; (2) bu — React'ning eng toza, eng arzon (Context/wrapper'siz) usuli, shuning uchun birinchi navbatda shuni o'ylang, keyin murakkabroq naqshlarga (compound, HOC, render props) o'ting. React rasmiy hujjati ham buni ta'kidlaydi: "kompozitsiyani afzal ko'ring".
2.2. HOC nima — komponentni o'rovchi funksiya
HOC (Higher-Order Component) — komponentni OLIB, yangi (kuchaytirilgan) komponent qaytaruvchi funksiya:
const EnhancedComponent = withSomething(OriginalComponent);
// └─ HOC: komponent kiradi, komponent chiqadi (+ qo'shimcha imkoniyat)
TUZILISHI:
function withLoading(Component) { // HOC — komponent oladi
return function WithLoading({ isLoading, ...props }) { // yangi komponent qaytaradi
if (isLoading) return <Spinner />; // qo'shimcha mantiq (yuklanish)
return <Component {...props} />; // asl komponent (props uzatiladi)
};
}
const UserListWithLoading = withLoading(UserList); // kuchaytirilgan versiya
┌────────────────────────────────────────────────────────────┐
│ HOC: f(Component) yangi Component (qo'shimcha imkoniyat) │
│ (funksiya — komponent kiradi, komponent chiqadi) │
└────────────────────────────────────────────────────────────┘
"Higher-order" — funksiyani/komponentni OLADIGAN funksiya (2.15 HOF — JS)
HOC komponentni O'RAB, unga props/mantiq "qo'shadi" (loading, auth, theme)HOC (Higher-Order Component) — komponentni olib, yangi (kuchaytirilgan) komponent qaytaruvchi funksiya. Nom "higher-order"dan keladi (2.15 — funksional dasturlashdagi higher-order function — funksiyani oluvchi/qaytaruvchi funksiya). HOC:
const Enhanced = withSomething(Original)—withSomethingHOC, uOriginalkomponentni olib, qo'shimcha imkoniyatli yangi komponent qaytaradi. Tuzilishi:function withLoading(Component) { return function(props) { if (props.isLoading) return <Spinner/>; return <Component {...props} /> } }—withLoadingHOC, u har qanday komponentga "yuklanish" mantig'ini qo'shadi (isLoadingbo'lsa spinner, aks holda asl komponent). Ishlatish:const UserListWithLoading = withLoading(UserList). Ikki nuqta: (1) HOC — funksiya (komponent kiradi, komponent chiqadi), JSX komponenti emas; (2) u komponentni o'rab, unga umumiy mantiq/props "qo'shadi" — loading, auth, theme, ma'lumot. Konvensiya — HOC nomiwithbilan boshlanadi (withAuth,withTheme,withRouter— eski React Router). HOC — hooklardan oldingi davrning asosiy mantiq-ulashish naqshi edi.
2.3. HOC yozish va ishlatish
HOC NAQSHLARI:
1. PROPS QO'SHISH (komponentga ma'lumot/imkoniyat beradi):
function withUser(Component) {
return function (props) {
const user = useAuth().user; // mantiqni bajaradi
return <Component {...props} user={user} />; // user prop'ini QO'SHADI
};
}
const ProfileWithUser = withUser(Profile); // Profile endi "user" prop oladi
2. SHARTLI RENDER (yuklanish, ruxsat):
function withAuth(Component) {
return function (props) {
const { user } = useAuth();
if (!user) return <Navigate to="/login" />; // himoya 11.9-bob
return <Component {...props} />;
};
}
3. KOMPOZITSIYA (bir nechta HOC):
const Enhanced = withAuth(withLoading(withTheme(MyComponent))); // ichma-ich (2.4 muammosi)
HOC props'ni {...props} bilan UZATADI (asl komponent o'z props'ini olsin)
HOC ichida hook ishlatsa bo'ladi (zamonaviy HOC) — lekin ko'pincha custom hook afzal (2.4)HOC yozish va ishlatish — bir nechta naqsh. (1) Props qo'shish — komponentga ma'lumot/imkoniyat berish:
withUser(Component)— asl komponentgauserprop'ini qo'shadi (<Component {...props} user={user} />— mavjud props'ni saqlab, yangisini qo'shadi). (2) Shartli render — yuklanish, ruxsat tekshiruvi:withAuth(Component)— foydalanuvchi yo'q bo'lsa login'ga yo'naltiradi (himoya — 11.9), aks holda komponentni ko'rsatadi. (3) Kompozitsiya — bir nechta HOC'ni birga ishlatish:withAuth(withLoading(withTheme(MyComponent)))— har biri o'z imkoniyatini qo'shadi (lekin ichma-ich — 2.4 muammosini keltiradi). Ikki muhim qoida: (1) HOC props'ni{...props}bilan uzatadi — asl komponent o'z props'ini yo'qotmasin (HOC faqat qo'shimcha qo'shadi, mavjudni bloklamaydi); (2) zamonaviy HOC ichida hook ishlatsa bo'ladi, lekin ko'p holda custom hook 11.7-bob HOC'dan ancha toza — shuning uchun yangi kodda mantiq uchun HOC kam yoziladi 2.4-bob. HOC'ni asosan o'qiy olish uchun bilib qo'yish kerak (eski kod, kutubxonalar).
2.3b. HOC — displayName, ref forwarding, props forwarding
YAXSHI HOC UCHUN UCH DETAL:
1. displayName — DevTools'da tushunarli nom (aks holda "Anonymous"/"WithLoading"):
function withLoading(Component) {
function WithLoading({ isLoading, ...props }) { ... }
const name = Component.displayName || Component.name || "Component";
WithLoading.displayName = `withLoading(${name})`; // DevTools: withLoading(UserList)
return WithLoading;
}
2. PROPS FORWARDING — {...props} bilan asl props'ni uzatish (HOC yutmasin — 2.3):
return <Component {...props} extra={...} />; // mavjud props saqlanadi
3. REF FORWARDING — HOC ref'ni "yutadi" (wrapper komponent ref'ni ushlaydi):
const WithLoading = forwardRef(function ({ isLoading, ...props }, ref) {
if (isLoading) return <Spinner />;
return <Component ref={ref} {...props} />; // ref asl komponentga o'tsin
});
// forwardRef'siz: <Enhanced ref={r}/> wrapper'ga tegadi (asl DOM/komponentga emas)
displayName — debug uchun majburiy (aks holda DevTools'da noaniq daraxt)
ref forwarding — HOC ref'ni asl komponentga o'tkazishi kerak (aks holda ref ishlamaydi)HOC — displayName, ref forwarding, props forwarding — sifatli HOC uchun uch detal. (1) displayName — HOC ichki komponentiga tushunarli nom berish. HOC yangi komponent yaratganda, React DevTools'da u anonim yoki noaniq (
WithLoading) ko'rinadi — daraxtni o'qish qiyin. Yechim:WithLoading.displayName = \withLoading(${Component.displayName || Component.name})`— DevTools'dawithLoading(UserList)ko'rinadi (qaysi HOC qaysi komponentni o'ragani aniq). (2) **props forwarding** — HOC asl props'ni{...props}bilan uzatishi shart (2.3 — aks holda asl komponent o'z props'ini yo'qotadi). (3) **ref forwarding** — bu eng ko'p unutiladigan detal: HOC komponentni o'raganda,bilan berilganref**wrapper**'ga tegadi (asl komponentga yoki uning DOM'iga emas) — chunkirefoddiy prop emas, uni React alohida ushlaydi. Yechim: HOC'niforwardRefbilan o'rab,ref'ni asl komponentga qo'lda uzatish (<Component ref={ref} {...props} />). Ikki xulosa: (1)displayName— debug uchun deyarli majburiy (aks holda ko'p qatlamli daraxt tushunarsiz); (2)forwardRef— agar HOC bilan o'ralgan komponentga tashqaridanref` berilishi kutilsa, majburiy. Bu detallar HOC'ni to'g'ri yozishni murakkablashtiradi — yana bir sabab nega mantiq uchun custom hook 11.7-bob afzal: hook'da bu muammolarning hech biri yo'q (ref, props, displayName — hammasi tabiiy komponentniki bo'lib qoladi).
2.4. HOC muammolari — nega hook afzal
HOC MUAMMOLARI (nega hooklar uni almashtirdi):
1. WRAPPER HELL — ko'p HOC ichma-ich (debug/o'qish qiyin):
withAuth(withLoading(withTheme(withRouter(Component)))) // 4 qatlam — chalkash
// React DevTools'da: WithAuth > WithLoading > WithTheme > WithRouter > Component (shovqin)
2. PROPS TO'QNASHUVI (props collision) — ikki HOC bir xil prop nomini bersa:
withUserA(withUserB(C)) // ikkalasi "user" prop bersa — biri ikkinchisini bosadi (jim bug)
3. QAYERDAN KELDI noaniq — prop qaysi HOC'dan keldi? (manba ko'rinmaydi)
4. STATIK TURLAP (TypeScript) — HOC props turini "yo'qotadi" (murakkab generics)
CUSTOM HOOK YECHIMI 11.7-bob — barcha muammoni hal qiladi:
function Component() {
const { user } = useAuth(); // QAYERDAN — aniq (chaqiruv ko'rinadi)
const theme = useTheme(); // to'qnashuv yo'q (alohida o'zgaruvchi)
// wrapper yo'q, manba aniq, tur saqlanadi
}
Hook — mantiqni "wrapper"siz ulashadi (manba aniq, to'qnashuv yo'q — 11.7: 2.1)
Mantiq qayta ishlatish uchun: HOC EMAS custom hook (zamonaviy standart)HOC muammolari — nega hooklar HOC'ni (mantiq ulashish uchun) almashtirdi. To'rt muammo: (1) wrapper hell — ko'p HOC ichma-ich joylansa (
withAuth(withLoading(withTheme(C)))), kod chalkash bo'ladi va React DevTools'da ko'p keraksiz qatlam ko'rinadi (debug/o'qish qiyin); (2) props to'qnashuvi (collision) — ikki HOC bir xil prop nomini (user) bersa, biri ikkinchisini jim bosib ketadi (topish qiyin bug); (3) manba noaniq — komponent prop'i qaysi HOC'dan kelganini ko'rish qiyin (chaqiruv ko'rinmaydi); (4) TypeScript turlari — HOC props turlarini to'g'ri saqlash murakkab (generics bilan kurashish). Custom hook 11.7-bob bularning hammasini hal qiladi:const { user } = useAuth()— manba aniq (chaqiruv ko'rinadi), to'qnashuv yo'q (alohida o'zgaruvchi), wrapper yo'q, tur saqlanadi. Ikki xulosa: (1) hook mantiqni "wrapper"siz, manba aniq, to'qnashuvsiz ulashadi (11.7: 2.1); (2) shuning uchun mantiq qayta ishlatish uchun zamonaviy standart — HOC emas, custom hook. HOC hali ham ba'zan ishlatiladi (masalan komponentni butunlay o'rash kerak bo'lganda — error boundary HOC, analytics tracking), lekin kam.
2.5. Render props nima
RENDER PROPS — komponent o'z mantig'ini FUNKSIYA prop orqali ulashadi (UI'ni chaqiruvchi hal qiladi):
<DataProvider render={(data) => <h1>{data.name}</h1>} />
// └─ "render" prop — funksiya (mantiq UI; komponent mantiqni, chaqiruvchi UI'ni beradi)
TUZILISHI:
function MouseTracker({ render }) {
const [pos, setPos] = useState({ x: 0, y: 0 });
useEffect(() => {
const onMove = (e) => setPos({ x: e.clientX, y: e.clientY });
window.addEventListener("mousemove", onMove);
return () => window.removeEventListener("mousemove", onMove);
}, []);
return render(pos); // mantiq (pos) ni funksiyaga beradi UI chiqadi
}
// Ishlatish — UI'ni O'ZIM beraman (mantiq MouseTracker'da):
<MouseTracker render={(pos) => <p>Sichqoncha: {pos.x}, {pos.y}</p>} />
┌────────────────────────────────────────────────────────────┐
│ Render props: komponent MANTIQ beradi (pos); chaqiruvchi UI │
│ (funksiya prop orqali — mantiq va ko'rinish ajratiladi) │
└────────────────────────────────────────────────────────────┘
Render props — mantiqni komponent, UI'ni CHAQIRUVCHI beradi (moslashuvchan)
"render" — shunchaki konvensiya (istalgan funksiya prop bo'ladi — 2.7 children)Render props — komponent o'z mantig'ini funksiya prop orqali ulashadi (mantiqni komponent beradi, UI'ni chaqiruvchi hal qiladi).
<DataProvider render={(data) => <h1>{data.name}</h1>} />—renderprop — funksiya (mantiq natijasini olib, JSX qaytaradi). Tuzilishi:MouseTrackersichqoncha holatini (pos) kuzatadi (state + effect), lekin uni qanday ko'rsatishni o'zi hal qilmaydi —return render(pos)bilan mantiqni (pos) funksiyaga beradi, funksiya esa UI qaytaradi. Ishlatish:<MouseTracker render={(pos) => <p>{pos.x}, {pos.y}</p>} />— chaqiruvchi UI'ni (qanday ko'rsatish) o'zi beradi, mantiq (sichqoncha kuzatish)MouseTracker'da qoladi. Bu — mantiq va ko'rinishni ajratishning eski usuli (custom hook'dan oldin). Ikki nuqta: (1) render props moslashuvchan — bir xil mantiq (sichqoncha kuzatish) turli UI bilan ishlatiladi (chaqiruvchi UI'ni beradi); (2)render— shunchaki konvensiya (nom), istalgan funksiya prop ishlaydi — ko'pinchachildrenfunksiya sifatida ishlatiladi 2.7-bob. Render props — HOC'ning muqobili edi (wrapper hell'siz, lekin JSX'da ichma-ich — 2.8).
2.6. Render props yozish va children as function
CHILDREN AS FUNCTION — render props'ning eng keng shakli (children — funksiya):
function Toggle({ children }) {
const [on, setOn] = useState(false);
const toggle = () => setOn(o => !o);
return children({ on, toggle }); // children — FUNKSIYA (mantiqni beradi)
}
// Ishlatish — children funksiya (mantiq UI):
<Toggle>
{({ on, toggle }) => ( // children funksiyasi mantiqni oladi
<button onClick={toggle}>{on ? "Yoqilgan" : "O'chiq"}</button>
)}
</Toggle>
RENDER PROP vs CHILDREN FUNCTION (bir xil g'oya, sintaksis farqi):
<X render={(d) => <UI/>} /> // render prop
<X>{(d) => <UI/>}</X> // children as function (tabiiyroq JSX)
┌────────────────────────────────────────────────────────────┐
│ children as function: <Comp>{(data) => <UI/>}</Comp> │
│ (mantiq Comp'da, UI children funksiyasida — render prop turi)│
└────────────────────────────────────────────────────────────┘
children funksiya — render props'ning tabiiy JSX shakli (keng ishlatilgan)
Ikkalasi bir xil g'oya: mantiq komponentda, UI funksiyada (chaqiruvchi beradi)Render props yozish va children as function — render props'ning amaliy shakli. Children as function — render props'ning eng keng tarqalgan ko'rinishi:
renderprop o'rnigachildrenfunksiya bo'ladi.function Toggle({ children }) { ...; return children({ on, toggle }) }—Togglemantiqni (on,toggle)childrenfunksiyasiga beradi. Ishlatish —childrenfunksiya bo'ladi:<Toggle>{({ on, toggle }) => <button onClick={toggle}>{on ? "Yoqilgan" : "O'chiq"}</button>}</Toggle>— mantiqToggle'da, UI children funksiyasida (chaqiruvchi beradi). Bu —renderprop bilan bir xil g'oya, faqat sintaksis farqi:<X render={(d) => <UI/>} />(render prop) vs<X>{(d) => <UI/>}</X>(children function — tabiiyroq JSX, ko'proq ishlatilgan). Ikki nuqta: (1) children as function — render props'ning tabiiy JSX shakli (komponent ichida UI joylashtirilgandek ko'rinadi, lekin funksiya orqali mantiq oladi); (2) ikkalasi ham bir xil tamoyil — mantiq komponentda, UI funksiyada (chaqiruvchi beradi — moslashuvchan). Bu naqsh eski kutubxonalarda (React Router v5<Route render>, Formik, Downshift) juda keng edi, hozir ham ba'zan uchraydi.
2.7. Render props vs hooks
BIR XIL MANTIQ — render props (eski) vs custom hook (zamonaviy):
RENDER PROPS (ichma-ich, "pyramid of doom" ko'p mantiqda):
<Mouse>
{(pos) => (
<Theme>
{(theme) => (
<Auth>
{(user) => <div>...</div>} // 3 qatlam ichma-ich (chalkash)
</Auth>
)}
</Theme>
)}
</Mouse>
CUSTOM HOOK (yassi, toza — 11.7):
function Component() {
const pos = useMouse(); // yassi — ichma-ich yo'q
const theme = useTheme();
const user = useAuth();
return <div>...</div>;
}
┌────────────────────────────────────────────────────────────┐
│ Render props: JSX'da ichma-ich (ko'p mantiqda piramida) │
│ Custom hook: yassi, o'qish oson (mantiq uchun AFZAL — 11.7) │
└────────────────────────────────────────────────────────────┘
MANTIQ ulashish uchun — custom hook (render props'ning piramida muammosini yo'qotadi)
Render props HALI foydali: UI'ga bog'liq mantiq (masalan o'lcham/joylashuv beruvchi)Render props vs hooks — qaysi biri qachon. Render props muammosi — ko'p mantiq ulashilganda JSX'da ichma-ich joylashish ("pyramid of doom"):
<Mouse>{pos => <Theme>{theme => <Auth>{user => ...}</Auth>}</Theme>}</Mouse>— uch mantiq uch qatlam ichma-ich (chalkash, o'qish qiyin). Custom hook 11.7-bob buni hal qiladi — yassi:const pos = useMouse(); const theme = useTheme(); const user = useAuth()(ichma-ich yo'q, har biri alohida qator, o'qish oson). Demak mantiq ulashish uchun — custom hook afzal (render props'ning piramida muammosini yo'qotadi, xuddi HOC'ning wrapper hell'ini yo'qotgandek — 2.4). Ikki muhim nuqta: (1) mantiq ulashish uchun zamonaviy yo'l — custom hook (render props eski kodda qoladi); (2) lekin render props hali ham foydali bo'lgan holatlar bor — ayniqsa UI'ga bog'liq mantiq beradigan komponentlar (masalan o'lcham/joylashuv hisoblab beruvchi —<Measure>{({width}) => ...}</Measure>, yoki virtualization kutubxonalari —react-virtualized), va compound components 2.9-bob — bularda render props yoki bola-funksiya hali tabiiy. Demak render props o'lmagan, lekin oddiy mantiq uchun hook afzal.
2.8. Compound components nima
COMPOUND COMPONENTS — bir-biriga BOG'LIQ komponentlar OILASI (birga ishlaydi):
Misol — Tabs (yorliqlar):
<Tabs defaultValue="profil">
<Tabs.List>
<Tabs.Tab value="profil">Profil</Tabs.Tab> bola
<Tabs.Tab value="sozlama">Sozlamalar</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="profil">Profil kontenti</Tabs.Panel>
<Tabs.Panel value="sozlama">Sozlamalar kontenti</Tabs.Panel>
</Tabs>
XUSUSIYATLARI:
- <Tabs> — "ota" (umumiy holat: qaysi tab faol — Context bilan ulashadi — 2.10)
- <Tabs.Tab>, <Tabs.Panel> — "bolalar" (holatni Context'dan oladi)
- Foydalanuvchi ularni ERKIN joylashtiradi (tartib, qo'shimcha element — moslashuvchan)
┌────────────────────────────────────────────────────────────┐
│ Compound: bog'liq komponentlar oilasi (Tabs/Tab/Panel) │
│ holatni Context orqali yashirin ulashadi (foydalanuvchi bilmaydi)│
└────────────────────────────────────────────────────────────┘
Compound — <select>/<option> kabi (HTML'ning tabiiy naqshi — birga ishlaydi)
Tab, Accordion, Menu, Select, Modal — compound components uchun idealCompound components — bir-biriga bog'liq komponentlar oilasi, ular birgalikda ishlashga mo'ljallangan. Misol — Tabs (yorliqlar):
<Tabs>(ota),<Tabs.List>,<Tabs.Tab>,<Tabs.Panel>(bolalar). Bu HTML'ning tabiiy naqshiga o'xshaydi —<select>va<option>(ular birga ishlaydi, alohida ma'nosiz). Xususiyatlari: (1)<Tabs>— "ota", umumiy holatni boshqaradi (qaysi tab faol) va uni Context orqali bolalarga ulashadi 2.10-bob; (2)<Tabs.Tab>/<Tabs.Panel>— "bolalar", holatni Context'dan oladi (<Tabs>ichida bo'lishi shart); (3) foydalanuvchi komponentlarni erkin joylashtiradi — tartib, qo'shimcha element, o'rab oluvchi<div>qo'shishi mumkin (moslashuvchan — qattiq tuzilishga majburlamaydi). Ikki nuqta: (1) compound components — bog'liq komponentlar oilasi, holatni Context orqali yashirin ulashadi (foydalanuvchi Context haqida bilmaydi, faqat komponentlarni joylashtiradi); (2) qachon — Tab, Accordion, Menu, Select, Modal, RadioGroup kabi murakkab, ko'p-qismli UI uchun ideal. Bu — render props/hook qila olmaydi (UI komponentlari o'rtasida holat ulashish) — shuning uchun compound components hali ham eng yaxshi UI naqshi.
2.9. Compound components — Context bilan
COMPOUND IMPLEMENTATSIYASI — Context bilan holatni yashirin ulashish (11.5: 2.11):
const TabsContext = createContext();
function Tabs({ children, defaultValue }) {
const [active, setActive] = useState(defaultValue);
return (
<TabsContext.Provider value={{ active, setActive }}> {/* holat ulashadi */}
<div className="tabs">{children}</div>
</TabsContext.Provider>
);
}
function Tab({ value, children }) {
const { active, setActive } = useContext(TabsContext); // holatni oladi
return (
<button
className={active === value ? "active" : ""}
onClick={() => setActive(value)}
>{children}</button>
);
}
function Panel({ value, children }) {
const { active } = useContext(TabsContext);
return active === value ? <div>{children}</div> : null; // faqat faol panel
}
// Bolalarni ota'ga "biriktirish" (Tabs.Tab — qulay API):
Tabs.List = function ({ children }) { return <div className="tab-list">{children}</div>; };
Tabs.Tab = Tab;
Tabs.Panel = Panel;
Context — compound'ning "yashirin sim"i (ota holatni bolalarga — foydalanuvchi bilmaydi)
Tabs.Tab = Tab — bolalarni ota'ga biriktirish (qulay, guruhlangan API)Compound components — Context bilan — implementatsiyaning yuragi. Compound components holatni Context (11.5: 2.11) orqali yashirin ulashadi. Tuzilishi: (1)
TabsContextyaratiladi; (2)Tabs(ota) —activeholatni saqlaydi va<TabsContext.Provider value={{ active, setActive }}>bilan bolalarga ulashadi; (3)Tab(bola) —useContext(TabsContext)bilan holatni oladi, bosilgandasetActive(value); (4)Panel(bola) — faqat o'ziningvalue'i faol bo'lganda render bo'ladi. Bolalarni ota'ga biriktirish —Tabs.Tab = Tab,Tabs.Panel = Panel(komponentga property sifatida) — bu qulay, guruhlangan API beradi (<Tabs.Tab>— foydalanuvchiTabqayerga tegishliligini darrov ko'radi). Ikki nuqta: (1) Context — compound components'ning "yashirin sim"i: ota holatni bolalarga uzatadi, lekin foydalanuvchi Context haqida bilmaydi (faqat<Tabs>,<Tabs.Tab>joylashtiradi — ichki mexanizm yashirin); (2)Tabs.Tab = Tab— bolalarni ota'ga biriktirish (toza, guruhlangan API —Tabs.*). Bu naqsh — Radix UI, Reach UI, MUI kabi kutubxonalarning asosiy dizayni (ular compound components + Context'ga qurilgan). Bu — moslashuvchan, qayta ishlatiladigan UI komponentlari yaratishning eng kuchli usuli.
Eski usul — React.Children + cloneElement (Context'dan oldin). Context keng tarqalmasidan avval compound components holatni bolalarga React.Children.map bilan aylanib chiqib, har biriga cloneElement(child, { active, setActive }) orqali props "qo'shib" berardi:
function Tabs({ children, defaultValue }) {
const [active, setActive] = useState(defaultValue);
return React.Children.map(children, (child) =>
React.cloneElement(child, { active, setActive }) // har bolaga qo'lda props "in'ektsiya"
);
}Bu usulning jiddiy kamchiliklari bor: (1) faqat bevosita bolalar props oladi — agar <Tab> bir <div> ichiga o'ralsa, cloneElement unga yeta olmaydi (chuqurroqda "yo'qoladi"); (2) props'lar aniq ko'rinmaydi (implicit — qaysi bola qaysi prop'ni oladi noaniq); (3) TypeScript bilan turini to'g'rilash og'ir. Context yechimi (yuqoridagi zamonaviy usul) bularning hammasini hal qiladi: bolalar istalgan chuqurlikda bo'lishi mumkin (useContext daraxt bo'ylab yuqoriga qaraydi), tuzilish erkin, tur tabiiy. Shuning uchun zamonaviy compound components — cloneElement emas, Context bilan yoziladi; cloneElement'ni asosan eski kodni o'qish uchun bilib qo'ying.
2.10. Control props va state reducer (ilg'or)
CONTROL PROPS — komponent holatini OTA boshqarsin (controlled — 11.4: 2.11 g'oya):
// Uncontrolled (komponent o'z holatini boshqaradi):
<Tabs defaultValue="profil">...</Tabs>
// Controlled (ota holatni boshqaradi — value + onChange):
<Tabs value={active} onChange={setActive}>...</Tabs>
// ota qaysi tab faolligini NAZORAT qiladi (forma/URL bilan sinxron — 11.9 useSearchParams)
HAR IKKALASINI qo'llab-quvvatlash (moslashuvchan komponent):
function Tabs({ value, defaultValue, onChange, children }) {
const [internal, setInternal] = useState(defaultValue);
const active = value !== undefined ? value : internal; // controlled yoki internal
const setActive = (v) => { setInternal(v); onChange?.(v); };
// ...
}
STATE REDUCER (eng ilg'or — Kent C. Dodds) — foydalanuvchi HOLAT MANTIG'INI o'zgartira olsin:
<Tabs stateReducer={(state, action) => /* o'z qoidang */} > // holat o'zgarishiga aralashish
Control props — komponent ham mustaqil, ham boshqariladigan (eng moslashuvchan API)
State reducer — kutubxona foydalanuvchisiga holat ustidan NAZORAT (ilg'or, kam)Control props va state reducer — compound components'ning ilg'or moslashuvchanlik naqshlari. Control props — komponent holatini ota boshqarishiga imkon (controlled — 11.4: 2.11 g'oyasi). Ikki rejim: uncontrolled (
<Tabs defaultValue="profil">— komponent o'z holatini boshqaradi) va controlled (<Tabs value={active} onChange={setActive}>— ota holatni nazorat qiladi, masalan tab holatini URL bilan sinxronlash — 11.9useSearchParams). Yaxshi komponent ikkalasini ham qo'llab-quvvatlaydi: ichidavalue !== undefined ? value : internal—valueprop berilsa controlled, aks holda ichki holat (<input>ning controlled/uncontrolled mexanizmidek). State reducer (eng ilg'or — Kent C. Dodds naqshi) — komponentgastateReducerprop berib, foydalanuvchi holat o'zgarish mantig'iga aralashishiga imkon beradi (masalan "bu tab'ni tanlashni taqiqla"). Ikki nuqta: (1) control props — komponent ham mustaqil (uncontrolled — oddiy), ham boshqariladigan (controlled — moslashuvchan) bo'lishiga imkon beradi (eng yaxshi API dizayni); (2) state reducer — kutubxona foydalanuvchisiga holat ustidan to'liq nazorat (ilg'or, kam ishlatiladi, lekin Downshift kabi murakkab kutubxonalarda mavjud). Bular — kutubxona darajasidagi moslashuvchan komponent dizaynining cho'qqisi.
2.11. Prop getters va qachon qaysi pattern
PROP GETTERS — komponent kerakli props'ni "tayyorlab" beradi (kutubxonada keng — Downshift):
const { getInputProps, getItemProps } = useCombobox(...);
<input {...getInputProps()} /> // barcha kerakli prop (onChange, aria, ref) tayyor
<li {...getItemProps({ item })} /> // moslashuvchan + to'g'ri (a11y, hodisalar)
QAYSI PATTERN QACHON (xulosa):
┌──────────────────┬──────────────────────────────────────────┐
│ MANTIQ ulashish │ custom hook 11.7-bob — DEFAULT (zamonaviy) │
│ UI komponent oilasi│ compound components (Tab/Accordion — 2.9)│
│ Komponentni o'rash │ HOC (kam — error boundary, analytics) │
│ UI'ga bog'liq mantiq│ render props (o'lcham, virtualization) │
│ Moslashuvchan API │ control props + prop getters (kutubxona) │
└──────────────────┴──────────────────────────────────────────┘
Zamonaviy default: custom hook (mantiq) + compound components (UI oilasi)
HOC/render props — asosan ESKI kod va MAXSUS holatlar (tushunish uchun bil)Prop getters va qachon qaysi pattern — yakuniy xulosa. Prop getters — komponent foydalanuvchiga kerakli props'ni tayyorlab beradigan naqsh (kutubxonalarda keng — Downshift, React Table):
const { getInputProps } = useCombobox(...)<input {...getInputProps()} />— barcha kerakli prop (onChange,aria-*,ref, hodisalar) avtomatik tayyorlangan va to'g'ri (a11y, kuzatuv) — foydalanuvchi faqat spread qiladi (moslashuvchan + xatosiz). Qaysi pattern qachon (eng muhim xulosa): mantiq ulashish custom hook (default, zamonaviy — 11.7); UI komponentlar oilasi (Tab, Accordion, Select) compound components 2.9-bob; komponentni butunlay o'rash (error boundary, analytics tracking) HOC (kam); UI'ga bog'liq mantiq (o'lcham, virtualization) render props; moslashuvchan kutubxona API control props + prop getters. Ikki yakuniy nuqta: (1) zamonaviy default — custom hook (mantiq) + compound components (UI oilasi) — ko'p holda shu ikkisi yetadi; (2) HOC/render props — asosan eski kod va maxsus holatlar uchun (lekin ularni o'qiy va tushuna olish kerak — ko'p mavjud kod va kutubxona ishlatadi). Bu naqshlarning hammasini bilish — har qanday React kodni o'qiy olish va to'g'ri dizayn tanlashning kalitidir.
2.12. Provider pattern
PROVIDER PATTERN — global holat/xizmatni Context orqali daraxtga ulashish (12-QISM state):
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
const toggle = () => setTheme(t => t === "light" ? "dark" : "light");
return (
<ThemeContext.Provider value={{ theme, toggle }}>{children}</ThemeContext.Provider>
);
}
function useTheme() { // provider bilan birga custom hook (qulay API)
const ctx = useContext(ThemeContext);
if (!ctx) throw new Error("useTheme <ThemeProvider> ichida bo'lishi kerak");
return ctx;
}
// Ilova ildizida bir marta: <ThemeProvider><App/></ThemeProvider>
// Istalgan chuqurlikda: const { theme, toggle } = useTheme();
Provider + custom hook — global holat ulashishning standart jufti (theme, auth, i18n)
Compound Context'dan farqi: Provider ILOVA darajasida (global), compound bitta komponent oilasidaProvider pattern — global holat yoki xizmatni butun daraxtga (yoki uning bir qismiga) Context orqali ulashish naqshi. Bu compound components'dagi Context bilan bir xil mexanizm, lekin miqyosi boshqacha: compound Context bir komponent oilasi ichida (
<Tabs>ichida), Provider pattern esa ilova darajasida (theme, autentifikatsiya, til/i18n, bildirishnoma, savatcha). Tuzilishi ikki qismdan iborat: (1) Provider komponenti — holatni saqlaydi va<Context.Provider>bilan ulashadi (<ThemeProvider>), ilova ildizida bir marta joylashtiriladi; (2) maxsus custom hook — Context'ni o'qishning qulay va xavfsiz usuli (useTheme()), ichidaif (!ctx) throwbilan Provider tashqarisida ishlatilsa aniq xato beradi (undefined bilan jimgina buzilish o'rniga). Ikki nuqta: (1) Provider + custom hook jufti — React'da global holat ulashishning de-fakto standarti (ko'p kutubxona — React Query, Redux, React Router — shu naqshda<QueryClientProvider>,<Provider>beradi); (2) bu — Context'ni to'g'ridan-to'g'ri ishlatishdan afzal, chunki hook API'ni yashiradi va nazoratni beradi (masalan keyin Context ichki tuzilishini o'zgartirsangiz, faqat hook'ni yangilaysiz, foydalanuvchi kodi tegmaydi). 12-QISM'da bu naqsh (Context + provider) holat boshqaruvining asosi sifatida chuqurroq ko'riladi.
2.13. Presentational vs Container (eski naqsh — hook'lar bilan o'zgargan)
ESKI NAQSH (Dan Abramov, 2015) — mas'uliyatni ikki komponentga ajratish:
CONTAINER (aqlli) — MANTIQ: state, data fetching, event handler'lar (UI yo'q):
function UserListContainer() {
const { data, loading } = useFetch("/api/users");
return <UserListView users={data} loading={loading} />; // faqat UI'ga props uzatadi
}
PRESENTATIONAL (soqov) — faqat UI: props oladi, chizadi (state/effect yo'q):
function UserListView({ users, loading }) {
if (loading) return <Spinner/>;
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}
HOOK'LAR BUNI O'ZGARTIRDI: mantiq endi custom hook'da (alohida komponent shart emas)
function UserList() { const {data} = useUsers(); return <ul>...</ul>; } // bitta komponent yetadiPresentational vs Container — hook'lardan oldingi mashhur naqsh (Dan Abramov, 2015): har bir "ekran"ni ikki komponentga ajratish — container (aqlli — state, data fetching, event handler; UI'ni deyarli chizmaydi) va presentational (soqov — faqat props oladi va chizadi, o'z holati/effekti yo'q). Maqsad — mas'uliyatni ajratish (9-QISM SoC): UI mantiqdan mustaqil (osonroq test, qayta ishlatish, dizayner ishlashi mumkin). Masalan
UserListContainerma'lumotni oladi vaUserListView'ga props sifatida beradi;UserListViewesa faqat chizadi. Muhim yangilik: custom hook'lar bu naqshni asosan o'zgartirdi. Endi mantiqni alohida komponentga ajratish shart emas — uni custom hookga ajratish yetarli (function UserList() { const { data, loading } = useUsers(); ... }). Mantiq hook'da (qayta ishlatiladigan, test qilinadigan), UI komponentda — ikkisi bitta faylda, ammo baribir ajratilgan. Shuning uchun zamonaviy React'da toza "presentational/container" ajratmasi kamayadi; uning o'rnini "UI komponent + custom hook" egalladi. Naqshning g'oyasi (mantiq va ko'rinishni ajratish) esa hali ham to'g'ri va qimmatli — faqat vositasi (ikki komponent hook) o'zgardi.
2.14. Polymorphic komponent (as prop)
POLYMORPHIC KOMPONENT — qaysi HTML teg/komponent sifatida render bo'lishini `as` prop hal qiladi:
function Text({ as: Component = "span", children, ...props }) {
return <Component {...props}>{children}</Component>; // "as" — qaysi teg
}
<Text>oddiy span</Text>
<Text as="h1">sarlavha</Text> // <h1> sifatida
<Text as="label" htmlFor="email">Email</Text>
<Text as={Link} to="/uy">Bosh sahifa</Text> // hatto boshqa komponent sifatida ham
TS bilan (turini "as"ga moslash — ilg'or):
type TextProps<T extends React.ElementType> =
{ as?: T; children: React.ReactNode } & React.ComponentPropsWithoutRef<T>;
// <Text as="a" href="..."/> href tekshiriladi; <Text as="button" href/> TS xatosi
Polymorphic — bitta komponent, ko'p teg (Button/Text/Box — dizayn tizimlarida keng)
TS bilan to'g'ri qilish murakkab (generic + ComponentPropsWithoutRef) — 11.14Polymorphic komponent — bitta komponent turli HTML teg yoki boshqa komponent sifatida render bo'lishiga imkon beradigan naqsh; qaysi teg ekanini
asprop hal qiladi.function Text({ as: Component = "span", ...props }) { return <Component {...props} /> }— odatda<span>, lekin<Text as="h1">,<Text as="label">, hatto<Text as={Link}>(React RouterLink) sifatida ham ishlaydi. Bu — dizayn tizimlarida (design system) juda keng:Box,Text,Buttonkabi asosiy komponentlar bitta uslub/mantiq bilan, lekin turli semantik teglar bilan ishlatiladi (masalan<Button as="a" href="...">— ko'rinishi tugma, semantikasi havola). Ikki nuqta: (1) polymorphic komponent kod takrorini kamaytiradi (har teg uchun alohida komponent yozmaysiz) va a11y'ni yaxshilaydi (to'g'ri semantik teg tanlash imkoni); (2) TypeScript bilan to'g'ri turlash murakkab —asprop qiymatiga qarab ruxsat etilgan props o'zgaradi (as="a"hrefbor,as="button"hrefyo'q). Buni generic (<T extends React.ElementType>) vaReact.ComponentPropsWithoutRef<T>bilan ifodalanadi — bu mavzu 11.14 (TypeScript bilan React) da chuqurroq ko'riladi. Polymorphic komponent — yana bir isbot: React'da to'g'ri primitivlar (asosiy komponentlar) qurish keyingi hamma narsani soddalashtiradi.
3. Sintaksis — tez ma'lumotnoma
KOMPOZITSIYA (2.1b): function Card({children}){<div>{children}</div>} | <List ItemComponent={Row}/>
SLOT (2.1b): function L({header,children}){...} <L header={<Nav/>}>...</L>
HOC 2.2-bob: function withX(Comp) { return (props) => <Comp {...props} extra={...}/> }
const Enhanced = withX(MyComp)
HOC ref (2.3b): forwardRef((props,ref)=><Comp ref={ref} {...props}/>); W.displayName=`withX(...)`
RENDER PROPS 2.5-bob: function X({ render }) { return render(data) } <X render={d => <UI/>}/>
CHILDREN FN 2.6-bob: function X({ children }) { return children(data) } <X>{d => <UI/>}</X>
COMPOUND 2.9-bob: const Ctx=createContext(); function Parent({children}){<Ctx.Provider>...}
Parent.Child = Child; <Parent><Parent.Child/></Parent>
CONTROL PROPS 2.10-bob:value!==undefined ? value : internal // controlled yoki uncontrolled
PROP GETTERS 2.11-bob: const { getInputProps } = useX(); <input {...getInputProps()}/>
PROVIDER 2.12-bob: function XProvider({children}){<Ctx.Provider value={v}>{children}</Ctx.Provider>}
POLYMORPHIC 2.14-bob: function Text({as:C="span",...p}){return <C {...p}/>} <Text as="h1"/>
QAYSI 2.11-bob: mantiqhook | UI oilasicompound | o'rashHOC | UI mantiqrender props4. Batafsil kod namunalari
Misol 1 — HOC: withLoading (2.2, 2.3)
function withLoading(Component) {
return function WithLoading({ isLoading, ...props }) {
if (isLoading) return <Spinner />; // qo'shimcha mantiq
return <Component {...props} />; // asl komponent (props uzatiladi)
};
}
function UserList({ users }) {
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}
const UserListWithLoading = withLoading(UserList); // kuchaytirilgan versiya
// Ishlatish: <UserListWithLoading isLoading={loading} users={users} />
// isLoading bo'lsa Spinner, aks holda UserList (HOC mantiq qo'shdi — 2.2)Misol 2 — HOC: withAuth (himoya — 2.3)
import { Navigate } from "react-router-dom";
function withAuth(Component) {
return function WithAuth(props) {
const { user, loading } = useAuth(); // 11.7
if (loading) return <Spinner />;
if (!user) return <Navigate to="/login" replace />; // himoya (11.9)
return <Component {...props} user={user} />; // user prop qo'shadi
};
}
const ProtectedDashboard = withAuth(Dashboard);
// Dashboard endi himoyalangan + user prop oladi (eski React'da keng — endi ProtectedRoute 11.9)Misol 3 — HOC kompozitsiyasi va muammosi (2.4)
// Wrapper hell — ichma-ich HOC (chalkash, DevTools'da shovqin):
const Enhanced = withAuth(withLoading(withTheme(withAnalytics(MyComponent))));
// 4 qatlam — qaysi prop qayerdan? to'qnashuv? (2.4)
// Custom hook — yassi, manba aniq (zamonaviy yechim):
function MyComponent() {
const { user } = useAuth(); // manba aniq
const theme = useTheme(); // to'qnashuv yo'q
useAnalytics("MyComponent");
return <div>...</div>;
}
// Mantiq uchun — HOC kompozitsiyasi EMAS, custom hook (11.7, 2.4)Misol 4 — Render props: MouseTracker (2.5)
function MouseTracker({ render }) {
const [pos, setPos] = useState({ x: 0, y: 0 });
useEffect(() => {
const onMove = (e) => setPos({ x: e.clientX, y: e.clientY });
window.addEventListener("mousemove", onMove);
return () => window.removeEventListener("mousemove", onMove);
}, []);
return render(pos); // mantiqni (pos) funksiyaga beradi
}
// Bir xil mantiq, har xil UI:
<MouseTracker render={(pos) => <p>Sichqoncha: {pos.x}, {pos.y}</p>} />
<MouseTracker render={(pos) => <div style={{ left: pos.x, top: pos.y }} className="cursor" />} />
// Mantiq (kuzatish) MouseTracker'da; UI chaqiruvchida (moslashuvchan — 2.5)Misol 5 — Children as function: Toggle (2.6)
function Toggle({ children }) {
const [on, setOn] = useState(false);
const toggle = () => setOn(o => !o);
return children({ on, toggle }); // children — funksiya
}
// Ishlatish:
<Toggle>
{({ on, toggle }) => (
<div>
<button onClick={toggle}>{on ? " Yoqilgan" : " O'chiq"}</button>
{on && <p>Bildirishnomalar yoqilgan</p>}
</div>
)}
</Toggle>
// children funksiya — mantiq Toggle'da, UI funksiyada (render props turi — 2.6)Misol 6 — Render props custom hook (2.7)
// ESKI (render props):
// <MouseTracker render={(pos) => <UI pos={pos}/>} />
// ZAMONAVIY (custom hook — 11.7):
function useMouse() {
const [pos, setPos] = useState({ x: 0, y: 0 });
useEffect(() => {
const onMove = (e) => setPos({ x: e.clientX, y: e.clientY });
window.addEventListener("mousemove", onMove);
return () => window.removeEventListener("mousemove", onMove);
}, []);
return pos;
}
function Component() {
const pos = useMouse(); // yassi — ichma-ich yo'q (2.7)
const theme = useTheme();
return <div>{pos.x}, {pos.y}</div>;
}
// Hook — render props piramidasini yo'qotadi (mantiq uchun afzal — 2.7)Misol 7 — Compound components: Tabs (2.8, 2.9)
import { createContext, useContext, useState } from "react";
const TabsContext = createContext();
function Tabs({ children, defaultValue }) {
const [active, setActive] = useState(defaultValue);
return (
<TabsContext.Provider value={{ active, setActive }}>
<div className="tabs">{children}</div>
</TabsContext.Provider>
);
}
function TabList({ children }) { return <div className="tab-list" role="tablist">{children}</div>; }
function Tab({ value, children }) {
const { active, setActive } = useContext(TabsContext);
return (
<button role="tab" aria-selected={active === value}
className={active === value ? "active" : ""}
onClick={() => setActive(value)}>{children}</button>
);
}
function Panel({ value, children }) {
const { active } = useContext(TabsContext);
return active === value ? <div role="tabpanel">{children}</div> : null;
}
// Bolalarni ota'ga biriktirish (qulay API):
Tabs.List = TabList;
Tabs.Tab = Tab;
Tabs.Panel = Panel;
// Ishlatish — moslashuvchan, o'qish oson:
function App() {
return (
<Tabs defaultValue="profil">
<Tabs.List>
<Tabs.Tab value="profil">Profil</Tabs.Tab>
<Tabs.Tab value="sozlama">Sozlamalar</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="profil">Profil kontenti</Tabs.Panel>
<Tabs.Panel value="sozlama">Sozlamalar kontenti</Tabs.Panel>
</Tabs>
);
}
// Holat Context'da yashirin ulashiladi; foydalanuvchi erkin joylashtiradi (2.9)Misol 8 — Compound: Accordion (2.9)
const AccordionContext = createContext();
function Accordion({ children }) {
const [openId, setOpenId] = useState(null);
const toggle = (id) => setOpenId(prev => (prev === id ? null : id)); // bittasi ochiq
return (
<AccordionContext.Provider value={{ openId, toggle }}>
<div className="accordion">{children}</div>
</AccordionContext.Provider>
);
}
function Item({ id, title, children }) {
const { openId, toggle } = useContext(AccordionContext);
const isOpen = openId === id;
return (
<div className="acc-item">
<button onClick={() => toggle(id)} aria-expanded={isOpen}>{title} {isOpen ? "▲" : "▼"}</button>
{isOpen && <div className="acc-content">{children}</div>}
</div>
);
}
Accordion.Item = Item;
// <Accordion><Accordion.Item id="1" title="Savol 1">Javob</Accordion.Item></Accordion>Misol 9 — Control props (controlled + uncontrolled — 2.10)
function Tabs({ value, defaultValue, onChange, children }) {
const [internal, setInternal] = useState(defaultValue);
const active = value !== undefined ? value : internal; // controlled yoki internal
const setActive = (v) => {
if (value === undefined) setInternal(v); // uncontrolled — ichki holat
onChange?.(v); // controlled — otaga xabar
};
return (
<TabsContext.Provider value={{ active, setActive }}>
<div className="tabs">{children}</div>
</TabsContext.Provider>
);
}
// Uncontrolled: <Tabs defaultValue="a">...
// Controlled (URL bilan — 11.9): <Tabs value={tab} onChange={setTab}>...
// Bitta komponent — ham mustaqil, ham boshqariladigan (moslashuvchan API — 2.10)Misol 10 — Prop getters (2.11)
// Custom hook prop getters qaytaradi (Downshift uslubi):
function useAccordion() {
const [open, setOpen] = useState(false);
return {
isOpen: open,
getButtonProps: () => ({ // tugma uchun tayyor props
onClick: () => setOpen(o => !o),
"aria-expanded": open,
}),
getPanelProps: () => ({ // panel uchun tayyor props
hidden: !open,
role: "region",
}),
};
}
function FAQ() {
const { isOpen, getButtonProps, getPanelProps } = useAccordion();
return (
<div>
<button {...getButtonProps()}>Savol {isOpen ? "▲" : "▼"}</button> {/* a11y/hodisa tayyor */}
<div {...getPanelProps()}>Javob</div>
</div>
);
}
// Prop getters — kerakli props (hodisa, a11y) tayyor; foydalanuvchi spread qiladi (2.11)Misol 11 — HOC (maxsus holat: error boundary HOC — 2.11)
import { ErrorBoundary } from "react-error-boundary";
// HOC hali foydali: komponentni butunlay o'rab (error boundary — 11.12)
function withErrorBoundary(Component, fallback) {
return function (props) {
return (
<ErrorBoundary fallback={fallback}>
<Component {...props} />
</ErrorBoundary>
);
};
}
const SafeWidget = withErrorBoundary(Widget, <p>Widget xato</p>);
// HOC mantiq uchun kam, lekin "o'rab olish" (error boundary, analytics) uchun hali qulay (2.11)Misol 12 — Pattern'larni solishtirish (bir mantiq, 3 usul — 2.7)
// Bir xil "toggle" mantig'i — 3 pattern:
// 1. Custom hook (zamonaviy — DEFAULT):
function useToggle() { const [on, setOn] = useState(false); return [on, () => setOn(o => !o)]; }
// 2. Render props (children function):
function Toggle({ children }) { const [on, setOn] = useState(false); return children({ on, toggle: () => setOn(o => !o) }); }
// 3. HOC:
function withToggle(C) { return (props) => { const [on, setOn] = useState(false); return <C {...props} on={on} toggle={() => setOn(o => !o)} />; }; }
// Bir mantiq, 3 usul. Zamonaviy: hook (1). Eski: render props (2), HOC (3) — 2.7, 2.11Misol 13 — Compound + render props birga (moslashuvchan — 2.9, 2.6)
// Ba'zan compound + render props birga (eng moslashuvchan):
function Select({ children, defaultValue }) {
const [value, setValue] = useState(defaultValue);
const [open, setOpen] = useState(false);
return (
<SelectContext.Provider value={{ value, setValue, open, setOpen }}>
{children}
</SelectContext.Provider>
);
}
function SelectTrigger({ children }) {
const { value, open, setOpen } = useContext(SelectContext);
return (
<button onClick={() => setOpen(o => !o)}>
{typeof children === "function" ? children({ value, open }) : children} {/* render props ham */}
</button>
);
}
Select.Trigger = SelectTrigger;
// <Select.Trigger>{({ value }) => `Tanlangan: ${value}`}</Select.Trigger> // render props moslashuvchanlikMisol 14 — Qaysi pattern qachon (qaror jadvali — 2.11)
// MANTIQ ulashish (state, effect, data) CUSTOM HOOK:
function Component() { const data = useFetch(url); /* ... */ }
// UI KOMPONENT OILASI (Tab/Accordion/Select) COMPOUND COMPONENTS:
<Tabs><Tabs.Tab/><Tabs.Panel/></Tabs>
// KOMPONENTNI O'RASH (error boundary, analytics) HOC:
const Safe = withErrorBoundary(Component);
// UI'GA BOG'LIQ MANTIQ (o'lcham, virtualization) RENDER PROPS:
<Measure>{({ width }) => <Chart width={width}/>}</Measure>
// MOSLASHUVCHAN KUTUBXONA API CONTROL PROPS + PROP GETTERS:
const { getInputProps } = useCombobox();
// To'g'ri pattern — muammoga mos (hook default; compound UI; HOC/render props maxsus — 2.11)Misol 15 — Kompozitsiya: children, slot, komponent prop (2.1b)
// 1. children — o'rash (HOC'siz):
function Card({ title, children }) {
return (
<div className="card">
{title && <h3 className="card-title">{title}</h3>}
<div className="card-body">{children}</div>
</div>
);
}
// 2. slot — nomlangan JSX prop:
function PageLayout({ header, sidebar, children }) {
return (
<div className="layout">
<header>{header}</header>
<aside>{sidebar}</aside>
<main>{children}</main>
</div>
);
}
// 3. komponent prop — qaysi komponent chizishini chaqiruvchi hal qiladi:
function List({ items, ItemComponent }) {
return <ul>{items.map((x) => <ItemComponent key={x.id} item={x} />)}</ul>;
}
function App() {
return (
<PageLayout header={<Navbar />} sidebar={<Menu />}>
<Card title="Foydalanuvchilar">
<List items={users} ItemComponent={UserCard} />
</Card>
</PageLayout>
);
}
// Kompozitsiya ko'p muammoni HOC'siz/Context'siz hal qiladi — birinchi shuni o'ylang (2.1b)Misol 16 — HOC: displayName + ref forwarding (2.3b)
import { forwardRef } from "react";
function withLoading(Component) {
const WithLoading = forwardRef(function WithLoading({ isLoading, ...props }, ref) {
if (isLoading) return <Spinner />;
return <Component ref={ref} {...props} />; // ref asl komponentga o'tadi
});
const name = Component.displayName || Component.name || "Component";
WithLoading.displayName = `withLoading(${name})`; // DevTools: withLoading(Input)
return WithLoading;
}
const InputWithLoading = withLoading(Input);
// <InputWithLoading ref={inputRef} isLoading={loading} placeholder="Ism" />
// forwardRef'siz ref wrapper'ga tegardi; displayName DevTools daraxtini tushunarli qiladi (2.3b)Misol 17 — Provider pattern (Context + custom hook — 2.12)
import { createContext, useContext, useState } from "react";
const ThemeContext = createContext(null);
function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
const toggle = () => setTheme((t) => (t === "light" ? "dark" : "light"));
return (
<ThemeContext.Provider value={{ theme, toggle }}>
{children}
</ThemeContext.Provider>
);
}
function useTheme() {
const ctx = useContext(ThemeContext);
if (!ctx) throw new Error("useTheme <ThemeProvider> ichida chaqirilishi kerak");
return ctx; // { theme, toggle }
}
// Ildizda: <ThemeProvider><App /></ThemeProvider>
function ThemeButton() {
const { theme, toggle } = useTheme(); // istalgan chuqurlikda, xavfsiz
return <button onClick={toggle}>Mavzu: {theme}</button>;
}
// Provider + custom hook — global holat (theme/auth/i18n) ulashishning standart jufti (2.12)Misol 18 — Polymorphic komponent (as prop — 2.14)
function Text({ as: Component = "span", ...props }) {
return <Component {...props} />; // qaysi teg — "as" hal qiladi
}
// Bir komponent, ko'p teg:
<Text>oddiy matn</Text> {/* <span> */}
<Text as="h1" className="title">Sarlavha</Text> {/* <h1> */}
<Text as="label" htmlFor="email">Email</Text> {/* <label htmlFor> */}
<Text as={Link} to="/uy">Bosh sahifa</Text> {/* React Router Link sifatida */}
// Dizayn tizimlarida keng (Box/Text/Button); TS bilan turlash — 11.14 (2.14)5. To'g'ri va noto'g'ri holatlar
1) Mantiq ulashish
HOC/render props (wrapper hell / piramida — 2.4, 2.7)
custom hook (yassi, manba aniq — 11.7)2) UI komponent oilasi
bitta katta komponent + ko'p props (Tabs items prop bilan — qattiq)
compound components (Tabs.Tab — moslashuvchan, Context — 2.9)3) HOC props
HOC props'ni uzatmaslik (<Component user={user}/> — boshqa props yo'qoladi)
<Component {...props} user={user}/> (mavjudni saqla — 2.3)4) Compound holat
holatni har bolaga props bilan (drilling — qattiq)
Context bilan yashirin ulash (foydalanuvchi bilmaydi — 2.9)5) Control props
faqat uncontrolled (URL/forma bilan sinxron qilib bo'lmaydi)
controlled + uncontrolled ikkalasi (moslashuvchan — 2.10)6) Pattern tanlovi
hamma narsaga HOC (eski tafakkur — wrapper hell)
muammoga mos: hook (mantiq), compound (UI), HOC (o'rash) — 2.116. Keng tarqalgan xatolar va yechimlari
Xato 1 — HOC props yo'qoladi (asl komponent props olmaydi)
Sababi: {...props} uzatilmadi 2.3-bob. Yechimi: <Component {...props} extra={...} />.
Xato 2 — Wrapper hell (ko'p HOC — debug qiyin)
Sababi: mantiq uchun HOC kompozitsiyasi 2.4-bob. Yechimi: custom hook'ga ko'chir (Misol 3, 11.7).
Xato 3 — Compound component Context'siz ishlamaydi (Cannot read context)
Sababi: Tabs.Tab <Tabs> tashqarisida ishlatildi (Context yo'q — 2.9). Yechimi: bolalarni har doim ota ichida; hook'da tekshir (if (!ctx) throw).
Xato 4 — Render props piramida (ichma-ich, o'qish qiyin)
Sababi: ko'p render props ichma-ich 2.7-bob. Yechimi: custom hook'ga aylantir (Misol 6).
Xato 5 — Compound bolalar tartibi/joyi qattiq
Sababi: ota childrenni qattiq kutadi (faqat aniq tuzilish). Yechimi: Context bilan — bolalar erkin joylashsin (childrenni filter qilma — 2.9).
Xato 6 — Controlled/uncontrolled aralashib ketdi
Sababi: value ham, defaultValue ham, yoki ikkalasi noto'g'ri 2.10-bob. Yechimi: value !== undefined ? value : internal mantig'i (Misol 9).
Xato 7 — HOC TypeScript turlari yo'qoladi
Sababi: HOC props turini saqlamaydi 2.4-bob. Yechimi: custom hook (tur tabiiy saqlanadi), yoki HOC generics (murakkab).
7. Integratsiya — bu mavzu stack'ning qayerida uchraydi
- Custom hooks 11.7-bob: HOC/render props'ning zamonaviy muqobili (mantiq uchun).
- Kompozitsiya 11.2-bob: children/slot/komponent prop — HOC'siz qayta ishlatish (2.1b).
- Context (11.5: 2.11): compound components va provider pattern'ning yuragi (yashirin holat ulash).
- State boshqaruvi (12-QISM): provider pattern — global holat (theme/auth) ulashish 2.12-bob.
- Controlled inputs (11.4: 2.11): control props — komponent darajasida bir xil g'oya.
- forwardRef 11.6-bob: HOC ref forwarding — ref'ni asl komponentga o'tkazish (2.3b).
- Error boundary 11.12-bob: HOC maxsus holat (withErrorBoundary — Misol 11).
- Routing 11.9-bob: protected route — withAuth HOC'ning zamonaviy ko'rinishi.
- UI kutubxonalar 12.6-bob: Radix/MUI/Headless — compound + prop getters'ga qurilgan.
- TypeScript 11.14-bob: compound/hook — turlar tabiiy; HOC — murakkab generics.
- a11y 1.9-bob: compound (Tabs role/aria) — prop getters a11y'ni tayyorlaydi.
8. Eng yaxshi amaliyotlar (best practices)
- Avval kompozitsiya (children/slot/komponent prop — HOC/Context'dan oldin o'ylang — 2.1b).
- Mantiq custom hook (HOC/render props emas — zamonaviy default — 2.4, 2.7).
- UI oilasi compound components (Tab/Accordion/Select — moslashuvchan — 2.9).
- Compound — Context bilan,
cloneElementemas (chuqur bolalar, tur tabiiy — 2.9). - HOC — displayName + forwardRef (DevTools tushunarli, ref o'tsin — 2.3b).
- Global holat provider pattern (Context + custom hook jufti — 2.12).
- Compound — Context bilan (holat yashirin ulash; bolalar erkin — 2.9).
- HOC props'ni uzat (
{...props}— mavjudni saqla — 2.3). - HOC faqat o'rash uchun (error boundary, analytics — mantiq emas — 2.11).
- Control props (controlled + uncontrolled — moslashuvchan API — 2.10).
- Prop getters (kutubxonada — a11y/hodisa tayyor — 2.11).
- Pattern muammoga mos (hook/compound/HOC/render props — 2.11).
- Eski pattern'larni o'qiy ol (HOC/render props — mavjud kod/kutubxona).
- Kutubxona dizaynini o'rgan (Radix/Headless — compound + getter namunasi — 2.11).
9. Amaliy loyiha: "Qayta Ishlatiladigan UI Komponentlar To'plami"
Pattern'larni birlashtirib, moslashuvchan UI komponentlar kutubxonasini qurish.
Maqsad
Compound components, control props va prop getters bilan professional Tabs, Accordion va Select komponentlarini yaratish.
Talablar (requirements)
- Tabs (compound):
<Tabs>,<Tabs.List>,<Tabs.Tab>,<Tabs.Panel>— Context bilan (Misol 7, 2.9). - Accordion (compound): bir yoki ko'p element ochiq rejimi (Misol 8).
- Control props: Tabs ham controlled (
value/onChange), ham uncontrolled (defaultValue) — URL bilan sinxron (Misol 9, 2.10, 11.9). - Custom hook: kamida bitta mantiqni custom hook qil (useToggle/useDisclosure — Misol 12, 11.7).
- Prop getters: kamida bitta komponent prop getter qaytarsin (a11y tayyor — Misol 10, 2.11).
- a11y: compound komponentlar role/aria (tablist/tab/tabpanel — 1.9, 2.9).
- HOC (maxsus): withErrorBoundary HOC bilan komponentni o'rab himoyala (Misol 11, 11.12).
- Render props (moslashuvchan): kamida bitta joyda render props/children function (Misol 13, 2.6).
- Pattern tanlovi: har komponentda NEGA shu pattern'ni tanlaganingni izohla 2.11-bob.
- Qayta ishlatish: komponentlarni
components/ui/da, real sahifada ishlat.
Maslahatlar (hint)
- Compound — Context bilan (holat yashirin);
Tabs.Tab = Tabbilan biriktir (Misol 7). - Bolalarni
<Tabs>ichida ishlatishni hook'da tekshir (Context yo'q bo'lsa xato — Xato 3). - Control props:
value !== undefined ? value : internal(Misol 9, 2.10). - Mantiq uchun custom hook; UI oilasi uchun compound (aralashtirma — 2.11).
- a11y'ni boshidan (role/aria) — Radix/Headless namunasini ko'r 2.11-bob.
- HOC faqat "o'rash" uchun (error boundary) — mantiq uchun hook (Misol 11, 2.4).
"Tayyor" mezonlari (acceptance criteria)
- Tabs compound (List/Tab/Panel — Context).
- Accordion compound (ochiq/yopiq).
- Tabs controlled + uncontrolled (URL bilan sinxron).
- Kamida bitta custom hook (mantiq).
- Kamida bitta prop getter (a11y tayyor).
- Compound a11y (role/aria).
- withErrorBoundary HOC ishlatilgan.
- Render props/children function kamida bir joyda.
- Har pattern tanlovi oqlangan (NEGA — 2.11).
Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.
10. Xulosa va keyingi bobga ko'prik
Bu bobda React'ning klassik va zamonaviy pattern'larini chuqur o'rgandik:
- Nega pattern'lar (mantiq/UI qayta ishlatish — 2.1); kompozitsiya (children/slot/komponent prop — 2.1b).
- HOC (nima — 2.2, qanday — 2.3, displayName/ref/props forwarding — 2.3b, muammolar — 2.4).
- Render props (nima — 2.5, children function — 2.6, hooks bilan taqqos — 2.7).
- Compound components (nima — 2.8, Context bilan (va eski
cloneElement) — 2.9, control props/state reducer — 2.10, prop getters va qachon qaysi — 2.11). - Provider pattern 2.12-bob, presentational vs container 2.13-bob, polymorphic komponent (
asprop — 2.14).
Endi siz har qanday React kodni o'qiy olasiz (HOC/render props bo'lsa ham), va to'g'ri pattern tanlay olasiz: mantiq uchun custom hook, UI oilasi uchun compound components, maxsus holatlar uchun HOC/render props. Eng muhimi — UI kutubxonalar (Radix, MUI, Headless) qanday qurilganini tushunasiz.
Keyingi bob — 11.14-bob: TypeScript bilan React. 7-QISM'da TypeScript'ni o'rgandik — endi uni React bilan birlashtiramiz. Komponent va props turlari (interface/type, children, event), hooklar turlari (useState<T>, useRef, useReducer), generic komponentlar, event va form turlari, React.FC munozarasi, va 11.10'dagi Zod + TypeScript birikmasini chuqurlashtirish. TypeScript — zamonaviy professional React loyihasining standarti (xatolarni compile paytida tutadi, autocomplete, refactoring xavfsizligi).
Foydalanilgan rasmiy/ishonchli manbalar
- React rasmiy hujjati (react.dev) — "Passing Props to a Component", "Passing Data Deeply with Context", "Sharing State Between Components" (kompozitsiya, children, Context)
- React rasmiy hujjati (react.dev) — "Reusing Logic with Custom Hooks" (HOC/render props hook'larga o'tish)
- React eski hujjati (legacy.reactjs.org) — "Higher-Order Components", "Render Props", "Composition vs Inheritance"
- React rasmiy hujjati (react.dev) —
forwardRefva ref forwarding (HOC'da ref uzatish) - Kent C. Dodds — "Advanced React Patterns" / EpicReact (compound components, control props, state reducer, prop getters)
- Radix UI / Headless UI / Reach UI — compound components + prop getters + a11y arxitekturasi
- Chakra UI / Stitches — polymorphic komponent (
asprop) dizayni - Dan Abramov — "Presentational and Container Components" (naqsh va hook'lardan keyingi holati)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!