IlmHamroh
JavaScript Full-stack/17-qism. React asoslari18/45-dars20 daqiqa
Mundarija (32)

Formalar I: controlled inputlar — value, onChange va har bir input turi

Qisqacha: Controlled input — qiymati React state'idan keladigan maydon: value={name} va onChange={(e) => setName(e.target.value)}. Har harf onChange ni chaqiradi, state yangilanadi, React maydonni yangi qiymat bilan qayta chizadi. Shunda maydondagi matn har doim state'ga teng: uni tekshirish, o'zgartirish (katta harf), boshqa joyda ko'rsatish yoki tashqaridan tozalash oson. Checkbox va radio'da value emas — checked; <textarea> va <select> da ham value. type="number" maydon baribir satr qaytaradi. value ni onChange siz bersangiz — maydon qotib qoladi.

Bu darsda

  • Matn maydonini value + onChange bilan boshqarasiz.
  • textarea, select (bir va bir nechta tanlov), checkbox va radio ni controlled qilasiz.
  • Number input nega satr qaytarishini va uni qanday songa aylantirishni bilasiz.
  • Bitta onChange bilan ko'p maydonli formani boshqarasiz.
  • React'ning to'rtta forma ogohlantirishini o'qib, tuzatasiz.
  • vazifalar dagi tahrir maydonini controlled qilasiz.

Oldin bilishingiz kerak: State'ni ko'tarish, Hodisalarni boshqarish, Forma qiymatlarini o'qish va submit.

1. Nega bu kerak?

State'ni ko'tarish darsida "controlled komponent" ni ko'rdik: qiymat props'dan keladi, o'zgarish callback bilan so'raladi. Endi xuddi shu g'oya brauzerning eng oddiy elementiga — <input> ga qo'llanadi.

Vanilla JavaScript'da forma qiymatini qanday olardik? Forma qiymatlarini o'qish darsidagidek: yuborilganda input.value ni o'qiymiz. Bu yerda haqiqat manbai — DOM: maydondagi matnni faqat brauzer biladi. Kod uni so'raganda oladi.

Bu ba'zan yetarli. Lekin «Bahor» buyurtma formasida Jasur aka ko'proq narsa so'radi:

  • telefon maydoni yozilayotganda "9 ta raqam kerak" deb darhol ogohlantirsin;
  • promokod har doim katta harf bilan ko'rinsin;
  • "Mehmonlar soni" ga qarab narx darhol hisoblansin;
  • buyurtma yuborilgach, forma tozalansin.

Bularning hammasi "maydondagi qiymatni har lahzada bilish va o'zgartira olish" ni talab qiladi. Vanilla'da buning uchun input hodisasini tinglash, qiymatni o'qish, tekshirish va DOM'ga qaytarib yozish kerak. Ikki joyda haqiqat: o'zgaruvchida va maydonda. React'da maydon qiymatini state'ga beramiz — haqiqat bitta bo'ladi.

Hayotiy o'xshatish: oshxonadagi buyurtma taxtasi. Ofitsiant mehmon aytganini (hodisa) bosh oshpazga aytadi (setter). Oshpaz taxtaga yozadi (state). Zaldagi ekran esa doim taxtadagini ko'rsatadi (value). Ekranga hech kim to'g'ridan-to'g'ri yozmaydi.

2. Matn maydoni: value + onChange

2.1 Birinchi controlled input

jsx
import { useState } from "react";

function App() {
  const [name, setName] = useState("");
  return (
    <form>
      <label>
        Ismingiz{" "}
        <input
          value={name}
          onChange={(e) => setName(e.target.value)}
        />
      </label>
      <p>Salom, {name || "mehmon"}!</p>
      <p>Belgilar: {name.length}</p>
    </form>
  );
}

Ekranda:

text
Ismingiz
Salom, Malika!
Belgilar: 6

(Maydon ichidagi matn ekran natijasiga kirmaydi — u <p> lar orqali ko'rinadi.) Har harf uchun aylanish shunday:

sequenceDiagram
  participant U as Foydalanuvchi
  participant I as input
  participant R as React
  U->>I: "M" bosdi
  I->>R: onChange (e.target.value = "M")
  R->>R: setName("M") — yangi render
  R->>I: value = "M"
Qadam name Maydonda <p> da
boshida "" bo'sh Salom, mehmon!
"M" "M" M Salom, M!
… "Malika" "Malika" Malika Salom, Malika!

Ikki narsa birga ishlaydi: value={name} — "maydonda state'dagi qiymat tursin", onChange — "foydalanuvchi o'zgartirmoqchi bo'lsa, state'ni yangila". Ikkalasi bo'lmasa, controlled input ishlamaydi.

React'da onChange — har harfda ishlaydi, brauzerning input hodisasiga o'xshab. Vanilla'dagi change hodisasi esa maydondan chiqqandagina keladi. React ataylab shunday nomlagan — boshida chalg'itadi, keyin o'rganib qolasiz.

2.2 onChange siz value — qotib qolgan maydon

onChange ni unutsak:

jsx
function App() {
  return <input value="Osh" />;
}

Konsolda:

text
You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.

"Siz maydonga value berdingiz, lekin onChange ishlovchisini bermadingiz. Maydon faqat o'qish uchun bo'ladi. O'zgaradigan bo'lishi kerak bo'lsa — defaultValue ishlating. Aks holda onChange yoki readOnly qo'ying." Maydonga harf yozib ko'rsangiz — hech narsa o'zgarmaydi. React har bosishda maydonni yana "Osh" ga qaytaradi, chunki state o'zgarmadi.

Ogohlantirish uch yo'lni ko'rsatadi: maydon o'zgarishi kerak bo'lsa — onChange (controlled) yoki defaultValue (uncontrolled, keyingi darsda); faqat ko'rsatish uchun bo'lsa — readOnly.

2.3 Qiymatni o'zgartirib saqlash

Controlled input'ning kuchi — siz onChange da kelgan qiymatni o'zgartirib saqlay olasiz. Promokod har doim katta harf bilan:

jsx
import { useState } from "react";

function App() {
  const [code, setCode] = useState("");
  return (
    <>
      <label>
        Promokod{" "}
        <input
          value={code}
          onChange={(e) => setCode(e.target.value.toUpperCase())}
          maxLength={10}
        />
      </label>
      <p>Kod: {code}</p>
    </>
  );
}

Ekranda:

text
Promokod
Kod: BAHOR10

Foydalanuvchi kichik harf yozdi, maydonda esa katta harf turibdi. Telefon raqamidan faqat raqamlarni qoldirish ham shunday: e.target.value.replace(/\D/g, "") (RegExp asoslari darsidagi \D — "raqam bo'lmagan belgi").

2.4 Jonli tekshiruv

Qiymat state'da bo'lgani uchun xato xabari — hosilaviy qiymat:

jsx
import { useState } from "react";

function App() {
  const [phone, setPhone] = useState("");
  const digits = phone.replace(/\D/g, "");
  const error =
    digits.length > 0 && digits.length !== 9
      ? `9 ta raqam kerak, hozir ${digits.length} ta`
      : null;

  return (
    <>
      <label>
        Telefon{" "}
        <input
          value={phone}
          onChange={(e) => setPhone(e.target.value)}
          aria-invalid={error !== null}
          aria-describedby="phone-error"
        />
      </label>
      <p id="phone-error">{error}</p>
    </>
  );
}

Ekranda:

text
Telefon
9 ta raqam kerak, hozir 5 ta

error alohida state emas — har renderda phone dan hisoblanadi. aria-invalid va aria-describedby — Qulay va accessible forma darsidagi juftlik: Malikaning ekran o'quvchisi maydonga kelganda xato matnini ham o'qiydi.

Diqqat: Har harfda xato chiqarish ba'zan bezovta qiladi: foydalanuvchi hali yozib bo'lmagan. Ko'p formalarda xato maydondan chiqqanda (onBlur) yoki yuborishda ko'rsatiladi. Bu UX tanlovi — controlled input ikkalasiga ham imkon beradi.

Tekshirib ko'ring: onChange={(e) => setName(e.target.value.trim())} yozilsa, foydalanuvchi "Ali Valiyev" ni yoza oladimi?

Javob

Yo'q. "Ali" dan keyin bo'sh joy bosilganda "Ali " keladi, trim() uni "Ali" ga qaytaradi va maydonda bo'sh joy hech qachon paydo bo'lmaydi — "AliValiyev" bo'lib yoziladi. trim() ni yuborishda qiling, onChange da emas. Qoida: onChange dagi o'zgartirish yozishga xalaqit bermasin.

3. Har bir input turi

3.1 textarea

HTML'da <textarea> matni teglar orasida yoziladi. React'da — value bilan, xuddi <input> kabi:

jsx
import { useState } from "react";

function App() {
  const [note, setNote] = useState("");
  return (
    <>
      <label>
        Izoh{" "}
        <textarea
          value={note}
          onChange={(e) => setNote(e.target.value)}
          rows={3}
        />
      </label>
      <p>Qoldi: {200 - note.length} belgi</p>
    </>
  );
}

Ekranda:

text
Izoh Piyozsiz bo'lsin
Qoldi: 184 belgi

textarea dagi matn ekran natijasiga kiradi — u element ichidagi matn tuguni sifatida ham ko'rinadi (input qiymati esa kirmaydi). Eski HTML odatidagidek matnni ichiga yozsangiz — <textarea>salom</textarea> — React ogohlantiradi: Use the `defaultValue` or `value` props instead of setting children on <textarea>. ("Ichiga bola yozish o'rniga defaultValue yoki value ishlating").

3.2 select

HTML'da tanlangan variant <option selected> bilan belgilanadi. React'da — <select> ning o'zida value:

jsx
import { useState } from "react";

function App() {
  const [dish, setDish] = useState("osh");
  return (
    <>
      <label>
        Taom{" "}
        <select
          value={dish}
          onChange={(e) => setDish(e.target.value)}
        >
          <option value="osh">Osh</option>
          <option value="lagmon">Lag'mon</option>
          <option value="manti">Manti</option>
        </select>
      </label>
      <p>Tanlandi: {dish}</p>
    </>
  );
}

Ekranda:

text
Taom OshLag'monManti
Tanlandi: manti

(<option> matnlari ham ekran natijasiga kiradi — ular <select> ichidagi matn.) <option selected> yozsangiz, React aytadi: Use the `defaultValue` or `value` props on <select> instead of setting `selected` on <option>. Sabab bitta: tanlov — select ning qiymati, uni bitta joyda saqlash oson.

3.3 Bir nechta tanlov: multiple

multiple li select ning qiymati — massiv:

jsx
import { useState } from "react";

function App() {
  const [extras, setExtras] = useState(["non", "choy"]);

  function handleChange(e) {
    // selectedOptions — tanlangan <option> lar ro'yxati
    setExtras(Array.from(e.target.selectedOptions, (o) => o.value));
  }

  return (
    <>
      <select multiple value={extras} onChange={handleChange}>
        <option value="non">Non</option>
        <option value="choy">Ko'k choy</option>
        <option value="salat">Salat</option>
      </select>
      <p>Qo'shimcha: {extras.join(", ")}</p>
    </>
  );
}

Ekranda:

text
NonKo'k choySalat
Qo'shimcha: non, choy

e.target.value bu yerda faqat birinchi tanlanganini beradi — shuning uchun selectedOptions dan massiv yasaymiz (Array.from ning ikkinchi argumenti — har elementni o'zgartiruvchi funksiya, Array.from darsida). Kompyuterda bir nechtasini tanlash — Ctrl (macOS'da Cmd) bilan bosish. Telefonda esa bunday ro'yxat noqulay — ko'pincha checkbox'lar guruhi afzal.

3.4 Checkbox: checked

Checkbox'ning "qiymati" — belgilangan yoki yo'q. Shuning uchun value emas, checked, va e.target.value emas, e.target.checked:

jsx
import { useState } from "react";

function App() {
  const [noOnion, setNoOnion] = useState(false);
  return (
    <>
      <label>
        <input
          type="checkbox"
          checked={noOnion}
          onChange={(e) => setNoOnion(e.target.checked)}
        />
        Piyozsiz
      </label>
      <p>{noOnion ? "Oshpazga: piyozsiz!" : "Odatdagidek"}</p>
    </>
  );
}

Ekranda:

text
Piyozsiz
Oshpazga: piyozsiz!

Ko'p uchraydigan xato — e.target.value ni o'qish. Checkbox'ning value si — HTML atributi (sukut bo'yicha "on"), u belgilanganda ham, belgilanmaganda ham bir xil. onChange siz checked bersangiz — value dagidek ogohlantirish, faqat defaultChecked ni taklif qiladi.

3.5 Radio: bitta qiymat, ko'p tugma

Radio tugmalar guruhi — bitta state. Har tugma "men tanlanganmanmi?" ni state bilan solishtiradi:

jsx
import { useState } from "react";

const SIZES = [
  { value: "kichik", label: "Kichik", price: 25000 },
  { value: "orta", label: "O'rta", price: 35000 },
  { value: "katta", label: "Katta", price: 45000 },
];

function App() {
  const [size, setSize] = useState("orta");
  const price = SIZES.find((s) => s.value === size).price;

  return (
    <fieldset>
      <legend>Osh porsiyasi</legend>
      {SIZES.map((s) => (
        <label key={s.value}>
          <input
            type="radio"
            name="size"
            value={s.value}
            checked={size === s.value}
            onChange={(e) => setSize(e.target.value)}
          />
          {s.label}
        </label>
      ))}
      <p>Narx: {price} so'm</p>
    </fieldset>
  );
}

Ekranda:

text
Osh porsiyasi
KichikO'rtaKatta
Narx: 45000 so'm

checked={size === s.value} — tanlov hosilaviy: uchta boolean emas, bitta size (State strukturasi dagi status qoidasi). name — brauzer uchun guruh nomi (klaviatura bilan strelkalar orasida yurish uchun ham muhim — Bobur shunday tanlaydi). Radio'da onChange faqat tanlangan tugmada keladi — tanlovi bekor bo'lgan tugmada hodisa bo'lmaydi.

4. Number input — baribir satr

«Bahor» bron formasida mehmonlar soni. Sardor bir kishi qo'shib, stol hajmini hisoblamoqchi edi:

jsx
import { useState } from "react";

function App() {
  const [guests, setGuests] = useState("");
  return (
    <>
      <label>
        Mehmonlar{" "}
        <input
          type="number"
          min={1}
          value={guests}
          onChange={(e) => setGuests(e.target.value)}
        />
      </label>
      <p>Siz bilan birga: {guests + 1} kishi</p>
    </>
  );
}

Ekranda:

text
Mehmonlar
Siz bilan birga: 31 kishi

3 + 1 = 31! type="number" faqat klaviatura va tekshiruvni o'zgartiradi (telefonda raqamli klaviatura, strelkalar). e.target.value esa har doim satr — "3", va "3" + 1 — satrlarni ulash (Turlarni aniq o'zgartirish darsidagi + tuzog'i).

Tuzatish — state'da satrni saqlab, ishlatishda songa aylantirish:

jsx
import { useState } from "react";

function App() {
  const [guests, setGuests] = useState("");
  const count = Number(guests); // "" → 0, "3" → 3
  return (
    <>
      <label>
        Mehmonlar{" "}
        <input
          type="number"
          min={1}
          value={guests}
          onChange={(e) => setGuests(e.target.value)}
        />
      </label>
      <p>
        {guests === ""
          ? "Sonni kiriting"
          : `Jami: ${count + 1} kishi`}
      </p>
    </>
  );
}

Ekranda:

text
Mehmonlar
Jami: 4 kishi

Nega state'ni darhol songa aylantirmaymiz (setGuests(Number(e.target.value)))? Foydalanuvchi maydonni o'chirsa, Number("") — 0, va maydonda o'chirilgan joyda birdan 0 paydo bo'ladi. Yozishga xalaqit beradi. Satr — "foydalanuvchi nima yozgan bo'lsa, o'sha", son esa hisoblash uchun hosilaviy. Alternativa — e.target.valueAsNumber (bo'sh maydonda NaN).

Tekshirib ko'ring: Mehmonlar maydoniga 2.5 yozildi. Number(guests) nima qaytaradi va bu yerda nima qilish kerak?

Javob

2.5 — son, xato emas. Lekin yarim mehmon bo'lmaydi: Number.isInteger(count) bilan tekshirib, xato xabarini ko'rsating. Brauzerga ham ayting: step={1} — u holda brauzer tekshiruvi (:invalid) ham kasrni rad etadi (Brauzer validatsiyasi).

5. Ko'p maydonli forma: bitta onChange

Har maydonga alohida useState va alohida funksiya — beshta maydonda o'nta nom. Bog'liq qiymatlar bitta obyektda (State strukturasi dagi "guruhlash" qoidasi), ishlovchi esa bitta — name atributi bo'yicha:

jsx
import { useState } from "react";

function App() {
  const [form, setForm] = useState({
    name: "",
    table: "",
    childSeat: false,
  });

  function handleChange(e) {
    const { name, type, value, checked } = e.target;
    const next = type === "checkbox" ? checked : value;
    setForm({ ...form, [name]: next });
  }

  return (
    <form>
      <label>
        Ism{" "}
        <input
          name="name"
          value={form.name}
          onChange={handleChange}
        />
      </label>
      <label>
        Stol{" "}
        <input
          name="table"
          value={form.table}
          onChange={handleChange}
        />
      </label>
      <label>
        <input
          type="checkbox"
          name="childSeat"
          checked={form.childSeat}
          onChange={handleChange}
        />
        Bolalar stuli
      </label>
      <p>{JSON.stringify(form)}</p>
    </form>
  );
}

Ekranda:

text
Ism Stol Bolalar stuli
{"name":"Bobur","table":"7","childSeat":true}

[name]: … — hisoblanadigan kalit (computed kalitlar): name o'zgaruvchisidagi satr kalit bo'ladi. ...form — boshqa maydonlar saqlanadi (immutable yangilash). Maydonning name atributi state obyektidagi kalit bilan aynan bir xil bo'lishi shart — bitta harf farqi va yangi kalit paydo bo'ladi.

Katta formalarda (o'nlab maydon, murakkab tekshiruv) qo'lda yozish og'irlashadi. Buning uchun kutubxonalar bor — React Hook Form, 19-qismda. Ular ichida aynan shu darsdagi va keyingi darsdagi g'oyalar.

6. Ikki tuzoq: undefined va null

6.1 Uncontrolled'dan controlled'ga sakrash

useState() boshlang'ich qiymatsiz — undefined:

jsx
import { useState } from "react";

function App() {
  const [name, setName] = useState(); // ❌ undefined
  return (
    <label>
      Ism{" "}
      <input value={name} onChange={(e) => setName(e.target.value)} />
    </label>
  );
}

Konsolda:

text
A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://react.dev/link/controlled-components

"Komponent uncontrolled input'ni controlled'ga aylantiryapti. Ehtimol qiymat undefined dan aniq qiymatga o'zgargani sabab — bunday bo'lmasligi kerak. Input butun umri davomida controlled yoki uncontrolled bo'lishini hal qiling." React uchun value={undefined} — "value berilmagan", ya'ni uncontrolled. Birinchi harfdan keyin value="A" — controlled. Tuzatish: useState("").

Xuddi shu xato ma'lumot serverdan kelganda ham chiqadi: value={user.phone} va phone hali yo'q (undefined). Davosi — zaxira: value={user.phone ?? ""}.

6.2 null qiymat

jsx
function App() {
  return <input value={null} onChange={() => {}} />;
}

Konsolda:

text
`value` prop on `input` should not be null. Consider using an empty string to clear the component or `undefined` for uncontrolled components.

"input dagi value null bo'lmasligi kerak. Maydonni tozalash uchun bo'sh satr, uncontrolled uchun undefined ishlating." Ma'lumotlar bazasidan bo'sh maydon ko'pincha null bo'lib keladi — yana ?? "".

7. value yoki defaultValue?

value (controlled) defaultValue (uncontrolled)
Haqiqat manbai React state DOM (brauzer)
Har harfda render ha yo'q
Tashqaridan tozalash/o'zgartirish setX("") qiyin (ref yoki key)
Jonli tekshiruv, formatlash oson qiyin
Kod hajmi ko'proq kam
Checkbox/radio checked defaultChecked

Qachon controlled tanlanadi: qiymat yozilayotganda kerak bo'lsa — jonli tekshiruv, formatlash, boshqa maydonga ta'siri, tashqaridan tozalash. Qiymat faqat yuborilganda kerak bo'lsa, uncontrolled yetadi va soddaroq — keyingi darsda.

"Har harfda render — sekin emasmi?" Oddiy formada — yo'q: bir nechta element qayta chiziladi, bu mikrosoniyalar. Muammo faqat maydon juda katta komponentning state'iga bog'langanda chiqadi. Masalan, App dagi state'ga har harfda 1 000 qatorli ro'yxat qayta chizilsa. vazifalar da qidiruv aynan shunday — 1 000 vazifada o'lchab ko'rganmiz (mini-ilova darsi).

8. Vazifalar qadami: controlled tahrir maydoni

8.1 Oldin: matn DOM'da

Hozir (refactor/app-ildizi birlashtirilgan holat) tahrir maydoni — uncontrolled:

tsx
      {/* defaultValue — boshlang'ich qiymat, keyin maydon o'ziniki
          (uncontrolled). Fon yangilanganda React shu <input> ni
          QAYTA YARATMAYDI — yozilayotgan matn joyida qoladi */}
      <input
        id={TAHRIR_MAYDONI_ID}
        defaultValue={vazifa.matn}

"Saqlash" bosilganda asosiy.ts matnni DOM'dan qidiradi:

ts
function tahrirniSaqla(): void {
  const id = holat.tahrirId;
  const kiritma = tahrirMaydoni();
  if (id === null || kiritma === null) {
    return; // tahrir yopilgan — saqlanadigan narsa yo'q
  }
  const matn = kiritma.value.trim();

tahrirMaydoni() — document.getElementById("tahrir-maydon"). React chizgan elementni vanilla kod id bo'yicha qidiradi — ikki dunyo o'rtasidagi "yashirin ip". Id o'zgarsa yoki ikkita tahrir maydoni paydo bo'lsa, u jim buziladi.

8.2 Keyin: matn state'da

Yangi branch: refactor/formalar (commit — keyingi darsda, formalar bilan birga). TahrirQatori.tsx diff'i (kanondan):

diff
 export function TahrirQatori({ vazifa, onSaqla, onBekor }: Props) {
+  // Controlled (17/#18): maydondagi matn — state, maydon uni ko'rsatadi.
+  // Fon yangilanganda (server, boshqa tab) qator key bilan saqlanadi —
+  // yozilayotgan matn ham shu state'da qoladi. v6 da qator qayta
+  // yasalardi va matnni qo'lda tiklardik (fondaChiz)
+  const [matn, setMatn] = useState(vazifa.matn);
+
…
     if (event.key === "Enter") {
       event.preventDefault();
-      onSaqla();
+      onSaqla(matn);
…
       <input
         id={TAHRIR_MAYDONI_ID}
-        defaultValue={vazifa.matn}
+        value={matn}
+        onChange={(event) => setMatn(event.target.value)}
         maxLength={Vazifa.MAKS_UZUNLIK}
         aria-label="Vazifa matni"
         onKeyDown={tugmaBosildi}
…
-        onClick={onSaqla}
+        onClick={() => onSaqla(matn)}

Uch narsa o'zgardi:

  1. useState(vazifa.matn) — State strukturasi dagi "ataylab nusxa": tahrir boshlanganda vazifa matni olinadi, keyin foydalanuvchi o'zi yozadi. Server vazifani boshqa matnga o'zgartirsa ham, yozilayotgan matn bosib ketilmaydi.
  2. onSaqla(matn) — matn callback argumenti bo'lib otaga ketadi (callback props). turlar.ts da tur: readonly onSaqla: (matn: string) => void;.
  3. asosiy.ts endi DOM'ga qaramaydi:
ts
// Matn — TahrirQatori state'idan (17/#18), DOM'dan emas
function tahrirniSaqla(yangiMatn: string): void {
  const id = holat.tahrirId;
  if (id === null) {
    return; // tahrir yopilgan — saqlanadigan narsa yo'q
  }
  const matn = yangiMatn.trim();

trim() — saqlashda, onChange da emas («Jonli tekshiruv» bo'limidagi savolni eslang). isComposing tekshiruvi (xitoy, yapon klaviaturasidagi tanlov oynasi) o'zgarmadi: Enter bosilganda onSaqla(matn) faqat yozish tugagan bo'lsa chaqiriladi.

8.3 Qidiruv va saralash — allaqachon controlled

State'ni ko'tarish darsida yozilgan QidiruvMaydoni endi tushunarli:

tsx
      {/* value — holatdan (controlled, #18 da batafsil): vazifa
          qo'shilganda yoki import'da holat qidiruvni tozalasa, maydon
          ham bo'shaydi. type="search": Esc va × ham — onChange keladi */}
      <input
        type="search"
        id="qidiruv"
        maxLength={100}
        autoComplete="off"
        value={qidiruv}
        onChange={(event) => onQidir(event.target.value)}
      />

Bu yerda state App da emas — vanilla do'konda, lekin qoida o'sha: value + onChange. v6 da qidiruvniYoz() har chizishda if (maydon.value !== holat.qidiruv) maydon.value = holat.qidiruv qilardi — qo'lda sinxronlash. Controlled maydon buni o'zi qiladi.

Yangi vazifa formasi esa uncontrolled qoladi — ataylab. Nega — keyingi darsda.

9. Ko'p uchraydigan xatolar

Belgi / xabar Sabab Davosi
maydonga yozib bo'lmaydi; without an onChange handler value bor, onChange yo'q onChange qo'shing yoki defaultValue/readOnly
changing an uncontrolled input to be controlled useState() yoki value={undefined} useState(""), ?? ""
should not be null value={null} ?? ""
checkbox doim "on" e.target.value o'qilgan e.target.checked
3 + 1 = 31 number input satr qaytaradi Number(x) yoki valueAsNumber
bo'sh joy yozib bo'lmaydi onChange da trim() yuborishda trim()
selected / textarea bolasi ogohlantirishi HTML odati select va textarea da value

10. Mashqlar

Mashqlar kurs/mashqlar/17/18-controlled-inputlar/ da.

1-mashq (oson): To'g'ri xususiyat

Bo'sh joylarni to'ldiring:

  • Checkbox holatini o'qish: e.target.
  • <select> da tanlangan variantni belgilash uchun prop:
  • type="number" maydonda e.target.value ning turi:
  • Bo'sh controlled matn maydoni uchun useState boshlang'ich qiymati:
Yechim

checked — checkbox'ning belgilangani; value — select ning o'zida (<option selected> emas); string — number input ham satr qaytaradi; "" — undefined emas, aks holda "uncontrolled'dan controlled'ga" ogohlantirishi.

2-mashq (o'rta): Choy buyurtmasi

Forma: choy turi (select: ko'k choy 5 000, qora choy 6 000), choynaklar soni (type="number", boshlang'ich "1"), "Limon qo'shilsin" checkbox (+2 000 bitta choynakka). Pastda — "Jami: N so'm" (hosilaviy). Soni bo'sh bo'lsa — "Sonni kiriting". Hammasi controlled bo'lsin.

Yechim
jsx
import { useState } from "react";

const PRICES = { kok: 5000, qora: 6000 };

function App() {
  const [tea, setTea] = useState("kok");
  const [count, setCount] = useState("1");
  const [lemon, setLemon] = useState(false);

  const n = Number(count);
  const total = n * (PRICES[tea] + (lemon ? 2000 : 0));

  return (
    <form>
      <label>
        Choy{" "}
        <select value={tea} onChange={(e) => setTea(e.target.value)}>
          <option value="kok">Ko'k choy</option>
          <option value="qora">Qora choy</option>
        </select>
      </label>
      <label>
        Choynak{" "}
        <input
          type="number"
          min={1}
          value={count}
          onChange={(e) => setCount(e.target.value)}
        />
      </label>
      <label>
        <input
          type="checkbox"
          checked={lemon}
          onChange={(e) => setLemon(e.target.checked)}
        />
        Limon
      </label>
      <p>{count === "" ? "Sonni kiriting" : `Jami: ${total} so'm`}</p>
    </form>
  );
}

Ekranda:

text
Choy Ko'k choyQora choyChoynak Limon
Jami: 16000 so'm

2 × (6 000 + 2 000) = 16 000. Jami — hosilaviy: uchta state'dan hisoblanadi. yoz("Choynak", "2") maydonni tozalab "2" yozadi.

3-mashq (qiyin): Bron formasi TypeScript'da

TypeScript'da bitta state obyektli forma: name (matn), guests (number input, satr), time (radio: "12:00", "18:00", "20:00"), smoking (checkbox: "Chekish zonasi"). Bitta handleChange — ChangeEvent<HTMLInputElement> turi bilan. Pastda xulosa: "Bobur, 4 kishi, 18:00" (chekish zonasi tanlansa — oxiriga ", chekish zonasi"). Ism bo'sh yoki mehmonlar 1–12 oralig'ida bo'lmasa — "Formani to'ldiring". Ishora: ChangeEvent — react dan tur importi (React + TS III da batafsil, hozir import type { ChangeEvent } from "react"; yetadi).

Yechim
tsx
import { useState } from "react";
import type { ChangeEvent } from "react";

type Booking = {
  name: string;
  guests: string;
  time: string;
  smoking: boolean;
};

const TIMES = ["12:00", "18:00", "20:00"];

export default function App() {
  const [form, setForm] = useState<Booking>({
    name: "",
    guests: "",
    time: "12:00",
    smoking: false,
  });

  function handleChange(e: ChangeEvent<HTMLInputElement>) {
    const { name, type, value, checked } = e.target;
    const next = type === "checkbox" ? checked : value;
    setForm({ ...form, [name]: next });
  }

  const n = Number(form.guests);
  const valid =
    form.name.trim() !== "" &&
    Number.isInteger(n) &&
    n >= 1 &&
    n <= 12;

  return (
    <form>
      <label>
        Ism{" "}
        <input
          name="name"
          value={form.name}
          onChange={handleChange}
        />
      </label>
      <label>
        Mehmonlar{" "}
        <input
          type="number"
          name="guests"
          value={form.guests}
          onChange={handleChange}
        />
      </label>
      {TIMES.map((t) => (
        <label key={t}>
          <input
            type="radio"
            name="time"
            value={t}
            checked={form.time === t}
            onChange={handleChange}
          />
          {t}
        </label>
      ))}
      <label>
        <input
          type="checkbox"
          name="smoking"
          checked={form.smoking}
          onChange={handleChange}
        />
        Chekish zonasi
      </label>
      <p>
        {valid
          ? `${form.name.trim()}, ${n} kishi, ${form.time}` +
            (form.smoking ? ", chekish zonasi" : "")
          : "Formani to'ldiring"}
      </p>
    </form>
  );
}

Ekranda:

text
Ism Mehmonlar 12:0018:0020:00Chekish zonasi
Bobur, 4 kishi, 18:00

Radio guruhida ham name="time" — u bir vaqtda brauzer guruhi va state kaliti. valid — hosilaviy. [name] kaliti string bo'lgani uchun TypeScript Booking dagi aniq kalitni tekshira olmaydi — bu usulning narxi; maydon nomlarini xatosiz yozing.

4-mashq: Vazifalar qadami

refactor/formalar branch'ini oching va «Vazifalar qadami» dagi o'zgarishlarni kiriting: TahrirQatori.tsx (useState, value/onChange, onSaqla(matn)), turlar.ts (onSaqla: (matn: string) => void), asosiy.ts (tahrirniSaqla(yangiMatn)). npm run tip va npm test toza bo'lsin. Commit hozircha qilinmaydi.

Yechim
  • npm run tip — TS2554: Expected 1 arguments, but got 0. chiqsa, biror joyda hali onSaqla() argumentsiz chaqirilgan (Enter yoki tugma).
  • Brauzerda: vazifani tahrirlang, harf qo'shing, ikkinchi tabda boshqa vazifani o'zgartiring — yozilayotgan matn joyida. «Bekor qilish» va qayta «Tahrirlash» — matn vazifaning asl matniga qaytadi (TahrirQatori yangidan yaratiladi, State'ning saqlanishi).
  • Vitest'dagi tahrir testlari o'tadi: ular foydalanuvchi kabi yozadi va «Saqlash» ni bosadi — maydonning ichki tuzilishiga qaramaydi.

11. Real ishda

  • Controlled — React'dagi standart yo'l. Qidiruv maydonlari, filtrlar, sozlamalar sahifalari, chat yozish maydoni — deyarli hammasi controlled. UI kutubxonalardagi value/onChange (Select, DatePicker, Slider) ham shu shartnoma.
  • Formatlash (telefon +998 90 …, karta raqami 8600 …) — controlled input bilan yoziladi yoki tayyor kutubxona bilan.
  • Formalar kutubxonalari — React Hook Form, TanStack Form — 19-qismda. Ular ko'pincha uncontrolled yo'lni ishlatadi (keyingi dars) va controlled'ni faqat kerakli maydonlarga qoldiradi.
  • Intervyu savollari: "Controlled va uncontrolled input farqi?", "Nega value ni onChange siz berish ogohlantirish beradi?", "Checkbox'ni qanday controlled qilasiz?", "A component is changing an uncontrolled input to be controlled xatosi nimadan?".

Xulosa

  • Controlled input: value={state} + onChange={(e) => setState(e.target.value)} — haqiqat React state'ida.
  • textarea va select da ham value; multiple — massiv (selectedOptions); checkbox va radio'da checked.
  • Number input satr qaytaradi: state — satr, hisob — Number().
  • Ko'p maydon — bitta obyekt va name atributi bo'yicha bitta ishlovchi.
  • useState(""), ?? "" — undefined/null ogohlantirishlariga davo; trim() — yuborishda.
  • vazifalar dagi tahrir maydoni controlled: matn TahrirQatori state'ida, saqlash DOM'ga qaramaydi.

Keyingi dars: Formalar II: uncontrolled inputlar, FormData va form action — qiymat faqat yuborishda kerak bo'lsa state shart emas: defaultValue, FormData, React 19 <form action> va nega vazifalar uni ishlatmaydi.

Manbalar

  • React: <input> — react.dev/reference/react-dom/components/input
  • React: <select>, <textarea> — react.dev/reference/react-dom/components/select, …/textarea
  • React: "Reacting to Input with State" — react.dev/learn/reacting-to-input-with-state
  • MDN: HTMLInputElement.valueAsNumber, HTMLSelectElement.selectedOptions — developer.mozilla.org
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Formalar I: controlled inputlar — value, onChange va har bir input turi — IlmHamroh