IlmHamroh
JavaScript Full-stack/18-qism. React chuqur31/42-dars17 daqiqa
Mundarija (28)

Komponent API'sini loyihalash: props nomlash, variant va native atributlar

Qisqacha: Yaxshi komponent API'si — kam va aniq props. Ko'rinish bitta variant bilan ("oddiy" | "asosiy" | "xavfli"), bir-biriga zid bo'la oladigan boolean'lar to'plami (primary, danger, outline) bilan emas. Qolgan hamma narsa — native atributlar: ComponentPropsWithoutRef<"button"> va ...rest orqali id, aria-*, onClick o'rganishsiz ishlaydi. className almashtirilmaydi, qo'shiladi. Standart qiymat xavfsiz bo'lsin (type="button"). Kontent — props emas, children (kompozitsiya). vazifalar da shu qoidalar bilan Tugma yasaymiz va uni 9 joyga qo'yamiz.

Bu darsda

  • Props'ni React va DOM odatlariga mos nomlaysiz.
  • Boolean prop portlashi nima ekanini va uni variant union bilan qanday davolashni ko'rsata olasiz.
  • Komponentni ComponentPropsWithoutRef<"button"> bilan kengaytirib, ...rest va className ni to'g'ri uzatasiz.
  • Xavfsiz standart qiymatlar va "konfiguratsiya o'rniga kompozitsiya" tamoyilini qo'llaysiz.
  • vazifalar ga Tugma komponentini qo'shib, uni ilovadagi tugmalarga joylaysiz.

Oldin bilishingiz kerak: Polymorphic komponentlar va asChild, Headless komponentlar va prop getters, Reducer, Immer va holat mashinasi fikrlashi.

1. Nega bu kerak?

Oldingi darsda useMenyu ning API'sini — getterlarni — loyihaladik. Endi oddiyroq, lekin har kuni ishlatiladigan narsaga qaytamiz: tugma.

vazifalar da hozir to'qqizta <button> bor. Har birida type="button" qo'lda yozilgan, o'chirish dialogidagi tugmada .xavfli klassi, «Qo'shish» tugmasi esa boshqalardan hech farq qilmaydi — dizayner uni "asosiy" qilishni so'radi. Sardorga topshiriq: Tugma komponentini yasash.

Uning birinchi varianti:

jsx
<Button primary>Qo'shish</Button>
<Button danger outline small>O'chirish</Button>
<Button primary danger>???</Button>

Bir oy o'tib Button da 11 ta props bor edi: primary, secondary, danger, outline, ghost, small, large, loading, icon, iconRight, fullWidth. Uchinchi qatorga qarang: primary danger — yashilmi yoki qizilmi? Kodni ochmasdan hech kim bilmaydi. Kod esa if (primary) … else if (danger) … — tartib tasodifiy.

Komponent API'si — boshqa dasturchilar (va bir oydan keyingi siz) bilan shartnoma. Uni bir marta yozasiz, yuz marta o'qiladi. Yomon shartnomani keyin o'zgartirish qimmat: har o'zgarish uni ishlatgan barcha joylarni buzadi.

Hayotiy o'xshatish: choyxonadagi buyurtma varaqasi. Yaxshi varaqada "choy: ko'k / qora" — bittasini belgilaysiz. Yomon varaqada ikki alohida katak: "ko'k choy " va "qora choy ". Ofitsiant ikkalasini belgilasa, oshpaz nima damlaydi? Varaqaning o'zi xato qilishga imkon beradi.

2. Props nomlash

Nomlar — API'ning eng ko'p o'qiladigan qismi. Qoida bitta: foydalanuvchi allaqachon biladigan nomni ishlating. React dasturchisi DOM va React odatlarini biladi:

Qoida
Native nom bor bo'lsa — o'sha disabled, onClick, type isDisabled, onPress, kind
Hodisa — on + nima bo'ldi onOpenChange, onSelect openHandler, handleSelect
Boolean — ijobiy shakl disabled, open notEnabled, hidden={!visible}
Kontent — children <Tugma>Saqlash</Tugma> <Tugma label="Saqlash" />
Bir xil ma'no — bir xil nom hamma joyda variant birida variant, birida type, birida kind
Qisqartma yo'q description desc, dscr

Ikkinchi qator #27 dagi uchlik bilan bog'liq: open / defaultOpen / onOpenChange. handle… — ishlovchi funksiyaning ichki nomi (handleClick), props nomi emas. Props — on…, uning qiymati — handle…: onClick={handleClick} (Hodisalarni boshqarish).

Izchillik alohida qoida. Loyihadagi o'nta komponentdan biri ko'rinishni type bilan, boshqasi variant bilan, uchinchisi color bilan bersa, har safar hujjatga qarash kerak. Dizayn tizimi bir lug'at tanlaydi va hamma komponentda shunga amal qiladi.

vazifalar da nomlar o'zbekcha, lekin xuddi shu odatlar bilan: variant, ochiq/boshlangichOchiq/onOchiqOzgardi, onTanla.

3. Boolean prop portlashi

3.1 Muammo — raqamlarda

Har yangi boolean props mumkin bo'lgan holatlarni ikki baravar oshiradi. primary va danger — 4 kombinatsiya; outline qo'shilsa — 8; ghost — 16. Ularning ko'pchiligi ma'nosiz (primary + ghost + danger?), lekin TypeScript ham, kod ham ularni taqiqlamaydi:

Ko'rinish props'lari: mumkin bo'lgan kombinatsiyalar
  • 1 boolean2 ta
  • 2 boolean4 ta
  • 3 boolean8 ta
  • 4 boolean16 ta
  • variant (4 qiymat)hammasi ma'noli4 ta

Manba: Hisob: n ta boolean — 2ⁿ kombinatsiya; bitta union props — qiymatlar soni

Grafikdagi oxirgi ustun — bitta variant props'i to'rt qiymat bilan: to'rt holat, hammasi ma'noli, ziddiyat imkonsiz. Bu — holat mashinasi darsidagi "imkonsiz holatlarni imkonsiz qilish" g'oyasi, endi props'da.

3.2 Ikki variant yonma-yon

jsx
function BoolButton({ primary, danger, children }) {
  // Qaysi biri yutadi? Faqat kod biladi
  const color = primary ? "yashil" : danger ? "qizil" : "kulrang";
  return <button data-color={color}>{children} ({color})</button>;
}

function VariantButton({ variant = "oddiy", children }) {
  return (
    <button data-variant={variant}>
      {children} ({variant})
    </button>
  );
}

function App() {
  return (
    <div>
      <p>
        <BoolButton primary danger>
          O'chirish
        </BoolButton>
      </p>
      <p>
        <VariantButton variant="xavfli">O'chirish</VariantButton>
      </p>
    </div>
  );
}

Ekranda:

text
O'chirish (yashil)
O'chirish (xavfli)

primary danger — o'chirish tugmasi yashil chiqdi: primary tekshiruvi birinchi turgani uchun. Xato jim, sahifada hech kim payqamaydi — foydalanuvchi esa xavfli amalni xavfsiz deb o'ylaydi. variant bilan bunday xato yozib bo'lmaydi: props bitta, qiymat bitta.

3.3 TypeScript qo'riqchisi

variant ni literal turlar union'i bilan tiplasak, noto'g'ri qiymat kompilyatsiyada ushlanadi:

tsx
import type { ComponentPropsWithoutRef } from "react";

type ButtonProps = ComponentPropsWithoutRef<"button"> & {
  variant?: "oddiy" | "asosiy" | "xavfli";
};

function Button({ variant = "oddiy", ...rest }: ButtonProps) {
  return <button data-variant={variant} {...rest} />;
}

function App() {
  return <Button variant="qizil">O'chirish</Button>;
}

Xato:

text
TS2322: Type '"qizil"' is not assignable to type '"asosiy" | "oddiy" | "xavfli" | undefined'.

""qizil" turi "asosiy" | "oddiy" | "xavfli" | undefined ga mos emas." Muharrir esa variant=" yozilishi bilan uchta to'g'ri qiymatni taklif qiladi. Nomga ham e'tibor bering: "xavfli", "qizil" emas. Variant — ma'no (bu amal xavfli), rang emas. Ertaga dizayner xavfli tugmalarni to'q sariq qilsa, kod o'zgarmaydi.

3.4 Ikkinchi o'q: size

Ko'rinish bir nechta mustaqil o'qqa bo'linsa, har o'q — alohida union props: variant (ma'no) va size ("kichik" | "orta" | "katta"). Ular bir-biriga zid emas: har qanday variant har qanday size bilan ma'noli. 3 × 3 = 9 kombinatsiya — hammasi to'g'ri.

Ba'zan o'qlar kesishganda maxsus qoida kerak bo'ladi: "kichik xavfli tugmada matn qalin bo'lsin". Bunday qoidalar compound variants deyiladi va ularni qo'lda if bilan yozish tezda chalkashadi. Dizayn tizimi: tokenlar va variantlar darsida buning uchun cva vositasini ko'ramiz.

Tekshirib ko'ring: loading — boolean. U ham variant ga qo'shilishi kerakmi (variant="yuklanmoqda")?

Javob

Yo'q. loading — ko'rinish turi emas, holat: xavfli tugma ham, asosiy tugma ham yuklanishi mumkin. U variant bilan mustaqil o'q, ya'ni alohida props. Boolean bo'lishi to'g'ri — u boshqa boolean'lar bilan to'qnashmaydi. Qoida: bir-birini istisno qiladigan qiymatlar — bitta union; mustaqil holatlar — alohida props.

4. Native atributlar: o'rgatmang, uzating

4.1 ComponentPropsWithoutRef<"button"> va ...rest

Tugma — <button> ning o'rami. Foydalanuvchi unga id, aria-label, value, form, onFocus, title berishni xohlaydi. Har birini alohida props qilib yozish — cheksiz ish va har safar nimadir esdan chiqadi. Yechim: komponent <button> qabul qiladigan hamma narsani qabul qilsin va o'zi ishlatmaydiganini elementga uzatsin:

tsx
type Props = ComponentPropsWithoutRef<"button"> & {
  variant?: TugmaVarianti;
};

export function Tugma({ variant = "oddiy", ...qolgan }: Props) {
  return <button data-variant={variant} {...qolgan} />;
}

ComponentPropsWithoutRef<"button"> — #29 dagi tur: <button> ning hamma atributlari. & bilan o'z props'ingiz qo'shiladi. ...qolgan (rest) — destrukturlashda nomlanmagan hamma narsa. Endi <Tugma aria-label="O'chirish: non olish" onClick={…}> — o'rganish shart emas, native'dagidek ishlaydi. Tugmani ishlatgan har kim allaqachon <button> ni biladi.

4.2 className — qo'shiladi

Komponentning o'z klassi bor (tugma). Foydalanuvchi o'zinikini bersa, ...qolgan oxirida yoyilsa — u komponentnikini o'chirib yuboradi, stil yo'qoladi. Shuning uchun className alohida olinadi va birlashtiriladi: className === undefined ? "tugma" : \tugma ${className}`. Natija: →class="tugma qoshish"`. Bu — Slot dagi birlashtirish qoidasining eng sodda holi.

4.3 Xavfsiz standart: type="button"

HTML'da <button> ning standart turi — submit. Forma ichidagi har qanday tugma bosilsa, forma yuboriladi. Buni o'zingiz ko'ring:

jsx
function Tugma({ type = "button", ...rest }) {
  return <button type={type} {...rest} />;
}

function App() {
  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        console.log("forma yuborildi!");
      }}
    >
      <input placeholder="Vazifa" />
      <button onClick={() => console.log("tozalash")}>
        Tozalash
      </button>{" "}
      <Tugma onClick={() => console.log("tozalash (Tugma)")}>
        Tozalash (Tugma)
      </Tugma>
    </form>
  );
}

Konsolda:

text
tozalash
forma yuborildi!
tozalash (Tugma)

Oddiy <button> "tozalash" bilan birga formani ham yubordi — yangi vazifa tasodifan qo'shildi. Tugma esa faqat o'z ishini qildi. Standart qiymat — eng ko'p uchraydigan holat uchun xavfsiz bo'lsin. Submit kerak bo'lsa, u aniq yoziladi: <Tugma type="submit">. Xavfli narsa aniq ko'rinsin, xavfsizi jim bo'lsin.

4.4 ref — oddiy props

React 19 da ref funksiya komponentga oddiy props bo'lib keladi (useRef II). Uni <button> ga uzatish kifoya — forwardRef kerak emas. vazifalar dagi AmalTugmasi aynan shu yo'l bilan fokus uchun ref oladi. Agar ref ni ...qolgan ichida qoldirsangiz, u ham uzatiladi. Kanon uni aniq nomlaydi — o'qiganda ko'rinib tursin.

5. Konfiguratsiya o'rniga kompozitsiya

Sardorning 11 props'idan ikkitasi — icon va iconRight. Keyin iconSize, iconColor, iconLabel keldi… Har bir kontent turi uchun yangi props. Compound komponentlar darsidagi Tabs items={…} muammosining aynan o'zi.

Kontent — children:

jsx
// ❌ konfiguratsiya: har kontent turi uchun props
<Button
  icon="trash"
  iconRight={false}
  iconSize={16}
  label="O'chirish"
/>

// ✅ kompozitsiya: kontentni foydalanuvchi o'zi yig'adi
<Tugma variant="xavfli">
  <TrashIcon aria-hidden="true" /> O'chirish
</Tugma>

Ikkinchi variantda Tugma ikonka haqida hech narsa bilmaydi. Ikonka chapda yoki o'ngda, kattami yoki kichik, <svg> mi yoki rasm — foydalanuvchining ishi. Komponentning props'lari faqat uning o'z qarorlari uchun qoladi: ko'rinish (variant) va xulq (type). Bu — "composition over configuration" (konfiguratsiya o'rniga kompozitsiya) tamoyili.

Qachon props kerak? Komponent kontentga xulq qo'shishi kerak bo'lganda. loading yaxshi misol: tugma o'zi aria-busy, disabled va aylanuvchi belgi qo'yishi kerak — bu kontent emas, holat.

6. API tekshiruv ro'yxati

Yangi komponentni ulashishdan oldin Jasur aka shu savollarni beradi:

Savol Yaxshi javob
Props soni? kam: faqat komponentning o'z qarorlari
Zid kombinatsiya bormi? yo'q: bir-birini istisno qiladiganlar — bitta union
Native atribut ishlaydimi? ha: ComponentPropsWithoutRef + ...rest
className, ref, style? qo'shiladi yoki uzatiladi, yo'qolmaydi
Standart qiymatlar? xavfsiz: type="button", variant="oddiy"
Kontent? children orqali
Nomlar? DOM va loyiha lug'ati bilan izchil
Kelajakda o'zgarsa? yangi props qo'shiladi, eskisi buzilmaydi

Oxirgi qator — semver (semantik versiyalash). Props'ni olib tashlash yoki ma'nosini o'zgartirish — buzuvchi o'zgarish (major). Kutubxonada bu yangi versiya, ilovada — hamma joyni bir PR'da o'zgartirish. Yangi ixtiyoriy props qo'shish — xavfsiz (minor). Shuning uchun boshida kam props bilan boshlang: qo'shish oson, olib tashlash qimmat.

7. Vazifalar qadami: Tugma (holat 33)

Yangi branch: refactor/tugma-tokenlar. Holat 33 uch darsni qamraydi: Tugma (shu dars), xususiyat papkasi (#32) va dizayn tokenlari (#33). Commit — #33 da.

7.1 src/ui/Tugma.tsx

tsx
// Tugma.tsx — ilovaning tugmasi (18/#31: komponent API'si).
// Qoidalar: (1) kam va aniq props — ko'rinish bitta "variant"
// bilan, "asosiymi", "xavflimi" kabi boolean'lar to'plami emas
// (ikkalasi true bo'lsa-chi?); (2) qolgan hamma narsa — oddiy
// <button> atributlari (ComponentPropsWithoutRef<"button">,
// ...qolgan): id, aria-*, value, onClick — o'rganish shart emas,
// native'dagidek; (3) xavfsiz standart: type="button" (formada
// tasodifan submit bo'lmasin — submit kerak bo'lsa, aniq
// yoziladi); (4) className qo'shiladi, almashtirmaydi; ref — prop
// (React 19). Ko'rinish — CSS'da data-variant bo'yicha
// (tokenlar, #33)
import type { ComponentPropsWithoutRef, Ref } from "react";

type TugmaVarianti = "oddiy" | "asosiy" | "xavfli";

type Props = ComponentPropsWithoutRef<"button"> & {
  variant?: TugmaVarianti;
  ref?: Ref<HTMLButtonElement> | undefined;
};

export function Tugma({
  variant = "oddiy",
  type = "button",
  className,
  ref,
  ...qolgan
}: Props) {
  return (
    <button
      ref={ref}
      type={type}
      className={
        className === undefined ? "tugma" : `tugma ${className}`
      }
      data-variant={variant === "oddiy" ? undefined : variant}
      {...qolgan}
    />
  );
}

Fayl boshidagi izoh — shu darsning qisqa xulosasi: to'rt qoida. Izoh telefonga sig'ishi uchun qayta bo'lindi, kod — aynan kanon. Kodning o'zida uchta detal:

  • data-variant, klass emas. .tugma[data-variant="xavfli"] — atribut bitta qiymatga ega bo'la oladi, ikkita variant bir vaqtda bo'lishi imkonsiz. Klasslar bilan (tugma xavfli asosiy) yana boolean portlashi qaytardi. Radix ham holatni shunday beradi: data-state="open".
  • "oddiy" uchun atribut yo'q. undefined — React atributni umuman yozmaydi. Oddiy tugma — .tugma ning o'zi, DOM toza.
  • ref?: Ref<…> | undefined — exactOptionalPropertyTypes uchun (#27 da ko'rgan sabab): AmalTugmasi ref'ni o'z props'idan uzatadi, u undefined bo'lishi mumkin.

CSS'da .xavfli klassi o'rniga:

css
/* 18/#31: Tugma variantlari (data-variant) */
.tugma[data-variant="xavfli"] {
  border-color: var(--rang-xato);
  color: var(--rang-xato);
}
.tugma[data-variant="asosiy"] {
  border-color: var(--rang-asosiy);
  background: var(--rang-asosiy);
  color: var(--rang-asosiy-ustida);
}

Ranglar allaqachon CSS o'zgaruvchilari — 17-qismdagi tema (qorong'i rejim). #33 da qolgan qiymatlar ham tokenlarga aylanadi.

7.2 To'qqiz joyga

Tugma oddiy <button> larning o'rnini egalladi. Uch xil holat bor. Oddiy almashtirish (Aloqa.tsx):

diff
-      <button
-        type="button"
+      <Tugma
         id="qayta-tugma"
         hidden={tarmoq !== "xato"}
         onClick={onQaytaUrin}
       >
         Qayta urinish
-      </button>
+      </Tugma>

type="button" qatori o'chdi — endi standart. id, hidden, onClick — native, ...qolgan orqali o'tadi. Asosiy variant (YangiVazifaForma.tsx, «Qo'shish»):

diff
-    <button type="submit" id="qoshish-tugma">
+    <Tugma type="submit" id="qoshish-tugma" variant="asosiy">
       {pending ? "Qo'shilmoqda…" : "Qo'shish"}
-    </button>
+    </Tugma>

Bu formaning haqiqiy submit tugmasi, shuning uchun type="submit" aniq yozilgan. Uchinchi holat — o'chirish dialogi (OchirishDialog.tsx):

diff
-          <button value="bekor">Bekor qilish</button>
-          <button value="ochir" className="xavfli">
+          {/* type="submit": method="dialog" formasi shu bilan
+              yopiladi (Tugma standarti — "button", 18/#31) */}
+          <Tugma type="submit" value="bekor">
+            Bekor qilish
+          </Tugma>
+          <Tugma type="submit" value="ochir" variant="xavfli">
             O'chirish
-          </button>
+          </Tugma>

Bu — eng qiziq joy. v7 da tugmalar type siz edi va bu ataylab: <form method="dialog"> ichida submit tugma dialogni yopadi va returnValue ga o'z value sini yozadi (dialog). Tugma ning standarti "button" — u bilan dialog yopilmay qolardi. Shuning uchun kanon type="submit" ni aniq yozdi va izohda sababini qoldirdi. "Xavfsiz standart" qoidasining ikkinchi tomoni: xavfli xulq kerak bo'lgan joyda u ko'rinib tursin.

Qolgan oltitasi: AmalTugmasi (har qatordagi «Bajarildi», «O'chirish», «Tahrirlash» — ref bilan), Eksport.tsx (ikki tugma), Import.tsx (type="submit"). Brauzer tekshiruvi 159/159 — ssenariy hech qayerda o'zgarmadi. Tashqi xulq o'sha, faqat ichki tuzilma o'zgardi — bu refaktorlash.

7.3 Nega Tugma polymorphic emas?

#29 dagi as va asChild bu yerda yo'q. vazifalar da tugma ko'rinishidagi havola yo'q, demak polymorphic API — hech kim ishlatmaydigan murakkablik. Kerak bo'lgan kuni qo'shiladi va bu buzuvchi o'zgarish emas: yangi ixtiyoriy props. «API tekshiruv ro'yxati» dagi oxirgi qator shu: kam props bilan boshlash va kerak bo'lganda qo'shish. Bu tamoyil YAGNI deb ham ataladi ("You Aren't Gonna Need It" — "bu sizga kerak bo'lmaydi").

8. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
Ikki "zid" props birga berildi, natija tasodifiy boolean'lar to'plami bitta variant union
aria-label, id ishlamaydi komponent faqat o'z props'ini oladi ComponentPropsWithoutRef + ...rest
Komponent stili yo'qoldi foydalanuvchi className i ustidan yozdi className ni birlashtiring
Tugma bosilsa forma yuboriladi <button> standarti — submit type="button" standart
Dialog tugmasi uni yopmaydi standart type="button" method="dialog" da type="submit" aniq
variant="qizil" — rang nomi ko'rinish nomi ma'no emas, rang ma'no: xavfli, asosiy

9. Mashqlar

Mashqlar kurs/mashqlar/18/31-komponent-api/ da. Har yechimni «Ishga tushir» bilan sinang.

1-mashq (oson): nomlarni tuzating

Har props uchun to'g'ri nomni yozing:

  • isDisabled →
  • clickHandler →
  • kind="danger" (boshqa komponentlarda variant) →
  • text="Saqlash" (tugma matni) →
Yechim

Native nom bor — disabled. Hodisa props'i — on + hodisa: onClick. Loyihada ko'rinish variant deb atalsa, hamma komponentda shunday — izchillik. Kontent props emas, children: <Tugma>Saqlash</Tugma> («Props nomlash» jadvali).

2-mashq (o'rta): Badge — boolean'lardan variantga

«Bahor» menyusida taom yonidagi belgi: <Badge isNew>, <Badge isSpicy>, <Badge isVegan>. Ikkitasi birga berilsa, nima chiqishi noaniq. Badge ni variant ("yangi" | "achchiq" | "vegan") bilan qayta yozing. Matn — children, className qo'shilsin, qolgan atributlar <span> ga o'tsin. Klass: badge badge-yangi kabi.

Kutilgan natija:

text
Osh yangi
Lag'mon achchiq
Yechim
jsx
import { useEffect } from "react";

function Badge({ variant, className, ...rest }) {
  const base = `badge badge-${variant}`;
  return (
    <span
      className={
        className === undefined ? base : `${base} ${className}`
      }
      {...rest}
    />
  );
}

function App() {
  useEffect(() => {
    for (const b of document.querySelectorAll(".badge")) {
      console.log(b.className, b.title);
    }
  }, []);
  return (
    <ul>
      <li>
        Osh <Badge variant="yangi">yangi</Badge>
      </li>
      <li>
        Lag'mon{" "}
        <Badge
          variant="achchiq"
          className="katta"
          title="Achchiq taom"
        >
          achchiq
        </Badge>
      </li>
    </ul>
  );
}

Konsolda:

text
badge badge-yangi 
badge badge-achchiq katta Achchiq taom

Ekranda:

text
Osh yangi
Lag'mon achchiq

variant — bitta qiymat, ziddiyat imkonsiz. className="katta" qo'shildi, badge saqlandi. title — Badge uni bilmaydi, ...rest orqali <span> ga o'tdi. Birinchi qator oxiridagi bo'sh joy — title yo'q (bo'sh satr). Matn children da: belgini boshqa tilga tarjima qilish yoki ikonka qo'shish uchun Badge ga tegish shart emas.

3-mashq (qiyin): TextField API'si

TextField komponentini loyihalang: label (majburiy matn), error (ixtiyoriy matn), qolgan hamma narsa — <input> atributlari. Talablar: id — useId bilan avtomatik (useId), lekin foydalanuvchi id bersa — uniki; error bo'lsa, aria-invalid="true" va aria-describedby xato matniga ulansin. App da ikki maydon: biri xatosiz, biri error="Telefon raqami noto'g'ri" bilan. Effektda ikkinchi input'ning aria-describedby si xato matni elementiga ishora qilishini tekshiring.

Ishora: label va error — komponentning o'z qarorlari (props), qolgani — ...rest. Native maydon atributlari — Formalar I.

Kutilgan natija:

text
xato bog'langan: true
Yechim
jsx
import { useEffect, useId } from "react";

function TextField({ label, error, id, ...rest }) {
  const autoId = useId();
  const inputId = id ?? autoId;
  const errorId = `${inputId}-xato`;
  return (
    <p>
      <label htmlFor={inputId}>{label}</label>{" "}
      <input
        id={inputId}
        aria-invalid={error ? true : undefined}
        aria-describedby={error ? errorId : undefined}
        {...rest}
      />
      {error && <span id={errorId}> {error}</span>}
    </p>
  );
}

function App() {
  useEffect(() => {
    const input = document.getElementById("telefon");
    const target = document.getElementById(
      input.getAttribute("aria-describedby"),
    );
    const linked = target?.textContent.trim() !== "";
    console.log("xato bog'langan:", linked);
  }, []);
  return (
    <form>
      <TextField label="Ism" name="ism" />
      <TextField
        label="Telefon"
        id="telefon"
        type="tel"
        defaultValue="+998"
        error="Telefon raqami noto'g'ri"
      />
    </form>
  );
}

Konsolda:

text
xato bog'langan: true

Ekranda:

text
Ism
Telefon Telefon raqami noto'g'ri

API'da faqat ikki o'z props'i — label va error. name, type, defaultValue — native, o'rganish shart emas. id — "xavfsiz standart": bermasangiz ham label input'ga bog'lanadi; bersangiz — sizniki (id ?? autoId). aria-invalid va aria-describedby ...rest dan oldin — foydalanuvchi ularni o'zgartira oladi. Bu ham qaror: masalan, u qo'shimcha tavsif matnini ulamoqchi bo'lishi mumkin. Xatoni o'chirib bo'lmaydigan qilmoqchi bo'lsangiz, tartibni almashtiring (#30 dagi getter qoidasi).

4-mashq: vazifalar qadami

refactor/tugma-tokenlar branch'ida src/ui/Tugma.tsx ni yarating va «To'qqiz joyga» bo'limidagidek almashtiring. CSS'da .xavfli ni .tugma[data-variant=…] ga o'zgartiring. Dialog tugmalariga type="submit" ni unutmang. npm test, npm run tip, npm run lint toza bo'lsin; brauzerda o'chirish dialogini sinab ko'ring. Commit — #33 da.

Yechim

Eng muhim tekshiruv — dialog: vazifani o'chirib ko'ring. «O'chirish» va «Bekor qilish» dialogni yopishi va to'g'ri javob berishi kerak. type="submit" unutilsa, tugma bosiladi, lekin dialog ochiq qoladi va konsolda xato yo'q — jim regressiya. Brauzer tekshiruvi (e2e) buni ushlaydi: o'chirish ssenariysi muvaffaqiyatsiz bo'ladi. grep -rn "<button" src/komponentlar — qolgan oddiy tugmalarni topish uchun. TemaTanlov va qator ichidagi katak — tugma emas yoki boshqa ko'rinishli: ularni o'z holida qoldiring.

10. Real ishda

  • Dizayn tizimi komponentlari: shadcn/ui Button (variant, size, asChild), MUI (variant, color, size), Chakra — hammasi union props va native atributlarni uzatish. Bu darsdagi qoidalar ularning hujjatlarida "API guidelines" bo'limida yoziladi.
  • Kod ko'rigi: "yangi boolean props" — PR'larda ko'p muhokama qilinadigan mavzu: "bu variant emasmi?", "kompozitsiya bilan bo'lmaydimi?".
  • Kutubxona yozish: API — ommaviy shartnoma; props'ni olib tashlash — major versiya. Storybook'da har variant alohida hikoya (story) bo'ladi (Storybook).
  • Intervyu savollari: "Yaxshi komponent API'si qanday bo'ladi?", "Boolean prop portlashi nima?", "Nega className ni birlashtirasiz?", "ComponentProps<'button'> nima beradi?".

Xulosa

  • Props nomlari — DOM va React odatlari bo'yicha: native nom, on… hodisa, ijobiy boolean, kontent — children, izchil lug'at.
  • Bir-birini istisno qiladigan ko'rinish props'lari — bitta union (variant); boolean'lar to'plami 2ⁿ holat va jim ziddiyat beradi.
  • Native atributlar uchun ComponentPropsWithoutRef<"button"> + ...rest; className birlashtiriladi; ref — oddiy props.
  • Standart qiymatlar xavfsiz (type="button"), xavfli xulq aniq yoziladi (type="submit").
  • Kompozitsiya konfiguratsiyadan yaxshi: ikonka va matn — children, props — faqat komponentning o'z qarorlari.
  • vazifalar holat 33: Tugma — variant → data-variant, 9 joyda, dialogda type="submit" aniq.

Keyingi dars: Katta frontend loyiha arxitekturasi — xususiyat papkalari, qatlamlar, ochiq API (index.ts) va import qoidalari.

Manbalar

  • React: "Passing Props to a Component" — react.dev/learn/passing-props-to-a-component
  • React TypeScript Cheatsheet: "ComponentProps" — react-typescript-cheatsheet.netlify.app
  • MDN: <button> type atributi — developer.mozilla.org/docs/Web/HTML/Element/button
  • Radix: "Styling" (data-state atributlari) — radix-ui.com/primitives/docs/guides/styling
  • Kanon: sinov/18/QADAMLAR.md (#31–#33), holatlar/33/src/ui/Tugma.tsx
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Komponent API'sini loyihalash: props nomlash, variant va native atributlar — IlmHamroh