Mundarija (32)
- Bu darsda
- 1. Nega bu kerak?
- 2. Uncontrolled maydon
- 2.1 defaultValue va name
- 2.2 FormData — butun forma bir martada
- 3. Brauzer validatsiyasi
- 3.1 required, pattern, min — tekin tekshiruv
- 3.2 O'z xabaringiz: noValidate va validity
- 4. React 19: form action
- 4.1 <form action={fn}>
- 4.2 useActionState — natija va kutish holati
- 5. Tuzoq: action'dan keyin forma o'zi tozalanadi
- 5.1 Xato bo'lsa ham
- 5.2 Yechim: qiymatni state orqali qaytarish
- 5.3 form.reset() va defaultValue
- 6. Nega vazifalar <form action> ni ishlatmaydi
- 6.1 O'lchov
- 6.2 Kanondagi yechim
- 7. Vazifalar qadami: FormData, eksport va import React'da
- 7.1 Yangi vazifa — FormData
- 7.2 Import — uncontrolled textarea
- 7.3 Eksport — controlled, lekin faqat o'qish uchun
- 7.4 Tekshiruv va commit
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): FormData natijasi
- 2-mashq (o'rta): "Fikr bildiring" formasi
- 3-mashq (qiyin): Bron — useActionState bilan, matn yo'qolmasin
- 4-mashq: Vazifalar qadami
- 10. Real ishda
- Xulosa
- Manbalar
Formalar II: uncontrolled inputlar, FormData va React 19 form action
Qisqacha: Qiymat faqat yuborishda kerak bo'lsa, state shart emas: maydon uncontrolled qoladi (
defaultValue,name), yuborilganda esanew FormData(form)bilan hammasi bir martada o'qiladi. Brauzerning o'z tekshiruvi (required,pattern,min) ham ishlaydi. React 19 da<form action={fn}>funksiyagaFormDatani o'zi beradi,useActionStateesa natija va "kutilmoqda" holatini boshqaradi. Lekin action tugagach React formani o'zi tozalaydi — tekshiruv xato topsa ham. Shuning uchunvazifalardagi yangi vazifa formasionSubmit+FormDatabilan yozilgan: biz buni o'lchab ko'rdik.
Bu darsda
- Uncontrolled maydonni
defaultValuevanamebilan yozasiz. FormData,Object.fromEntriesvagetAllbilan formani bir martada o'qiysiz.- Brauzer validatsiyasini React formasida ishlatasiz va
validityni o'qiysiz. <form action>vauseActionStateni yozasiz: natija, xato vaisPending.- Action'dan keyingi avtomatik tozalash va
form.reset()tuzoqlarini taniysiz. vazifalarda yangi vazifa va import formalariFormDataga o'tadi, eksport va import React'ga ko'chadi.
Oldin bilishingiz kerak: Formalar I: controlled inputlar, FormData, Brauzer validatsiyasi.
1. Nega bu kerak?
Oldingi darsda har maydonni state'ga bog'ladik. Bu kuchli, lekin narxi bor: har maydonga useState, value, onChange. Har harfda qayta render. «Bahor» ning "Fikr bildiring" formasini ko'raylik: ism, telefon, baho, izoh. Unda yozilayotganda hech narsa hisoblanmaydi va tekshirilmaydi — forma yuborilganda bir marta o'qiladi va serverga ketadi. To'rtta useState bu yerda ortiqcha ish.
FormData darsida vanilla JavaScript'da shunday qilgan edik: maydonlarga name beramiz, submit da new FormData(form) hammasini yig'adi. Brauzer qiymatlarni o'zi saqlaydi — u bunga yaxshi. React bu yo'lni ham qo'llab-quvvatlaydi. React 19 esa unga yangi qulaylik qo'shdi — form action.
Hayotiy o'xshatish: anketa. Controlled forma — har javobni yozayotganingizda yoningizda turib, ovoz chiqarib takrorlab boradigan kotib. Uncontrolled forma — anketani to'ldirib, oxirida topshirasiz, kotib esa uni bir martada o'qiydi. Ko'p hollarda ikkinchisi yetadi.
2. Uncontrolled maydon
2.1 defaultValue va name
Uncontrolled input — qiymati DOM'da (brauzerda) saqlanadigan maydon. React unga faqat boshlang'ich qiymatni beradi:
import { useState } from "react";
function App() {
const [sent, setSent] = useState(null);
function handleSubmit(e) {
e.preventDefault(); // sahifa qayta yuklanmasin
const data = new FormData(e.currentTarget);
setSent(data.get("name"));
}
return (
<form onSubmit={handleSubmit}>
<label>
Ism <input name="name" defaultValue="" />
</label>
<button>Yuborish</button>
<p>{sent === null ? "Hali yuborilmadi" : `Rahmat, ${sent}!`}</p>
</form>
);
}Ekranda:
Ism Yuborish
Rahmat, Malika!useState faqat natija uchun — maydon uchun emas. Malika yozayotganda React hech narsa qilmaydi, qayta render ham yo'q. Yuborilganda:
| Qadam | Maydon qiymati (DOM'da) | sent |
Ekranda |
|---|---|---|---|
| boshida | "" |
null |
Hali yuborilmadi |
| "Malika" yozildi | "Malika" |
null |
Hali yuborilmadi (render yo'q) |
| «Yuborish» | "Malika" |
"Malika" |
Rahmat, Malika! |
defaultValue — maydon yaratilganda yoziladigan boshlang'ich qiymat. Keyin u maydonning o'ziniki: defaultValue ni o'zgartirsangiz ham, allaqachon yaratilgan maydon o'zgarmaydi. Checkbox va radio uchun — defaultChecked, select uchun ham defaultValue.
e.currentTarget — ishlovchi ulangan element, ya'ni <form>. e.target emas: ichki elementdan ko'tarilgan hodisada target boshqa bo'lishi mumkin (Hodisa tarqalishi).
Maslahat: Bo'sh boshlang'ich qiymat uchun
defaultValue=""yozish shart emas — atributsiz maydon ham bo'sh. Bu yerda u "bu maydon ataylab uncontrolled" degan belgi bo'lib turibdi.
2.2 FormData — butun forma bir martada
Ko'p maydon, ichida checkbox'lar guruhi:
import { useState } from "react";
function App() {
const [result, setResult] = useState("—");
function handleSubmit(e) {
e.preventDefault();
const data = new FormData(e.currentTarget);
const order = {
...Object.fromEntries(data), // { name, table, extras }
extras: data.getAll("extras"), // bir nomli hammasi
};
setResult(JSON.stringify(order));
}
return (
<form onSubmit={handleSubmit}>
<label>
Ism <input name="name" />
</label>
<label>
Stol{" "}
<select name="table" defaultValue="7">
<option value="4">4</option>
<option value="7">7</option>
</select>
</label>
<label>
<input type="checkbox" name="extras" value="non" />
Non
</label>
<label>
<input type="checkbox" name="extras" value="choy" />
Choy
</label>
<button>Yuborish</button>
<p>{result}</p>
</form>
);
}Ekranda:
Ism Stol 47NonChoyYuborish
{"name":"Bobur","table":"7","extras":["non","choy"]}Uch narsaga e'tibor bering:
Object.fromEntries(data)—FormDatadagi juftliklardan oddiy obyekt yasaydi (Obyekt bo'ylab yurish). Lekin bir nomli bir nechta qiymatdan faqat oxirgisini oladi.data.getAll("extras")— bir nomli hamma qiymatlar massivi. Checkbox guruhi uchun shu kerak.- Belgilanmagan checkbox
FormDataga umuman kirmaydi. Shuning uchungetAllbo'sh massiv qaytaradi,getesanull.
FormData dagi hamma qiymat — satr (fayl maydonidan tashqari). Stol raqami "7", son emas. Oldingi darsdagi number input qoidasi bu yerda ham: hisoblashdan oldin Number().
Tekshirib ko'ring: Formada
nameatributisiz maydon bor. UFormDataga kiradimi?
Javob
Yo'q. FormData faqat name li, disabled bo'lmagan maydonlarni yig'adi. Uncontrolled formada name — maydonning "manzili": usiz qiymatni topib bo'lmaydi. Ko'p uchraydigan xato: id va <label htmlFor> bor, name esa unutilgan.
3. Brauzer validatsiyasi
3.1 required, pattern, min — tekin tekshiruv
Brauzer validatsiyasi darsidagi atributlar React'da ham ishlaydi. Maydon noto'g'ri bo'lsa, brauzer submit hodisasini umuman yubormaydi va maydon yonida o'z xabarini ko'rsatadi:
import { useState } from "react";
function App() {
const [sent, setSent] = useState(false);
return (
<form
onSubmit={(e) => {
e.preventDefault();
setSent(true);
}}
>
<label>
Telefon <input name="phone" required />
</label>
<button>Yuborish</button>
<p>{sent ? "Yuborildi" : "Kutilmoqda"}</p>
</form>
);
}Ekranda:
Telefon Yuborish
KutilmoqdaBo'sh required maydon — onSubmit chaqirilmadi. Chrome'da maydon yonida "Please fill out this field." (brauzer tiliga qarab o'zbekcha ham bo'lishi mumkin) qalqib chiqadi. Bu matnni brauzer yozadi, React emas.
3.2 O'z xabaringiz: noValidate va validity
Brauzer xabari dizaynga mos kelmasa yoki o'zbekcha bo'lishi kerak bo'lsa: formaga noValidate qo'yasiz (brauzer to'xtatmaydi, lekin atributlar tekshiruvi ishlayveradi) va natijani validity dan o'qiysiz:
import { useState } from "react";
function App() {
const [error, setError] = useState(null);
function handleSubmit(e) {
e.preventDefault();
const phone = e.currentTarget.elements.namedItem("phone");
if (phone.validity.valueMissing) {
setError("Telefon raqamini kiriting");
} else if (phone.validity.patternMismatch) {
setError("9 ta raqam, bo'sh joysiz: 901234567");
} else {
setError(null);
}
}
return (
<form onSubmit={handleSubmit} noValidate>
<label>
Telefon{" "}
<input
name="phone"
required
pattern="[0-9]{9}"
inputMode="tel"
/>
</label>
<button>Yuborish</button>
<p role="alert">{error ?? "Hammasi to'g'ri"}</p>
</form>
);
}Ekranda:
Telefon Yuborish
9 ta raqam, bo'sh joysiz: 901234567validity — har maydonda bor obyekt: valueMissing (bo'sh required), patternMismatch, rangeUnderflow (min dan kichik), typeMismatch (type="email" ga email emas) va boshqalar. elements.namedItem("phone") — formadagi name="phone" maydon. vazifalar dagi yangi vazifa formasi aynan noValidate bilan: xato matnini forma o'zi chiqaradi.
Diqqat: Brauzer tekshiruvi — faqat qulaylik. Hujumchi DevTools'da
requiredni o'chirib yoki so'rovni to'g'ridan-to'g'ri yuborib uni chetlab o'tadi. Haqiqiy tekshiruv serverda bo'lishi shart (Forma xavfsizligi).
4. React 19: form action
4.1 <form action={fn}>
React 19 da <form> ning action atributiga funksiya berish mumkin. React yuborishni o'zi ushlaydi (preventDefault shart emas) va funksiyaga tayyor FormData ni beradi:
import { useState } from "react";
function App() {
const [dishes, setDishes] = useState(["Osh"]);
function addDish(formData) {
setDishes([...dishes, formData.get("dish")]);
}
return (
<form action={addDish}>
<label>
Taom <input name="dish" />
</label>
<button>Qo'shish</button>
<p>Menyu: {dishes.join(", ")}</p>
</form>
);
}Ekranda:
Taom Qo'shish
Menyu: Osh, MantionSubmit bilan solishtiring: e.preventDefault() yo'q, new FormData(e.currentTarget) yo'q — React ikkalasini qildi. Yana bir farq ko'zga ko'rinmaydi: action tugagach, React formadagi uncontrolled maydonlarni tozaladi. "Manti" yozilgan maydon endi bo'sh — keyingi taomni yozishga tayyor. Ko'p hollarda aynan shu kerak.
Action funksiyasi async ham bo'lishi mumkin (serverga fetch). React uni transition ichida bajaradi — bu "shoshilinch emas" yangilanish. Tafsiloti — Actions darsida.
4.2 useActionState — natija va kutish holati
Haqiqiy forma serverga ketadi: kutish vaqti bor, javob muvaffaqiyat yoki xato. Bularni boshqarish uchun React 19 da useActionState hook'i:
const [state, formAction, isPending] =
useActionState(action, initialState);action(prevState, formData)— sizning funksiyangiz. U yangi state'ni qaytaradi (masalan,{ error: "…" }).initialState— birinchi renderdagistate.state— action oxirgi marta qaytargan qiymat.formAction—<form action={formAction}>ga beriladigan o'ralgan funksiya.isPending— action bajarilayotganmi.
Bron so'rovi, server o'rniga 300 ms kutish bilan:
import { useActionState } from "react";
async function book(prevState, formData) {
await new Promise((r) => setTimeout(r, 300)); // "server"
const name = formData.get("name").trim();
if (name === "") return { error: "Ism kerak", ok: null };
return { error: null, ok: `${name}, stol bron qilindi!` };
}
function App() {
const [state, formAction, isPending] = useActionState(book, {
error: null,
ok: null,
});
return (
<form action={formAction}>
<label>
Ism <input name="name" />
</label>
<button disabled={isPending}>Bron qilish</button>
{isPending && <p>Yuborilmoqda…</p>}
{state.error && <p role="alert">{state.error}</p>}
{state.ok && <p>{state.ok}</p>}
</form>
);
}Ekranda:
Ism Bron qilish
Yuborilmoqda…Tugma bosilgandan 60 ms keyin — action hali tugamagan: isPending true. Shu blok kut(400) bilan:
import { useActionState } from "react";
async function book(prevState, formData) {
await new Promise((r) => setTimeout(r, 300)); // "server"
const name = formData.get("name").trim();
if (name === "") return { error: "Ism kerak", ok: null };
return { error: null, ok: `${name}, stol bron qilindi!` };
}
function App() {
const [state, formAction, isPending] = useActionState(book, {
error: null,
ok: null,
});
return (
<form action={formAction}>
<label>
Ism <input name="name" />
</label>
<button disabled={isPending}>Bron qilish</button>
{isPending && <p>Yuborilmoqda…</p>}
{state.error && <p role="alert">{state.error}</p>}
{state.ok && <p>{state.ok}</p>}
</form>
);
}Ekranda:
Ism Bron qilish
Malika, stol bron qilindi!| Qadam | isPending |
state |
Ekranda |
|---|---|---|---|
| boshida | false |
{ error: null, ok: null } |
forma |
| bosildi | true |
o'sha | Yuborilmoqda… (tugma bloklangan) |
| 300 ms dan keyin | false |
{ ok: "Malika, …" } |
Malika, stol bron qilindi! |
Bitta hook uchta state'ning o'rnini bosdi: natija, xato va "kutilmoqda". State strukturasi darsida qo'lda yozgan status ning tayyor shakli. Tugmaning disabled={isPending} — ikki marta bosishdan himoya. Ichki tugma komponentida isPending ni olish uchun useFormStatus bor (react-dom dan) — useActionState va useFormStatus darsida.
5. Tuzoq: action'dan keyin forma o'zi tozalanadi
5.1 Xato bo'lsa ham
Endi tekshiruv xato topgan holat. Mehmon ismni juda qisqa yozdi:
import { useActionState } from "react";
function check(prevState, formData) {
const name = formData.get("name");
if (name.length < 3) return { error: "Kamida 3 harf" };
return { error: null };
}
function App() {
const [state, formAction] = useActionState(check, { error: null });
function showField() {
const field = document.querySelector("[name=name]");
console.log("Maydonda:", JSON.stringify(field.value));
}
return (
<>
<form action={formAction}>
<label>
Ism <input name="name" />
</label>
<button>Yuborish</button>
<p>{state.error ?? "—"}</p>
</form>
<button onClick={showField}>Maydon?</button>
</>
);
}Konsolda:
Maydonda: ""Ekranda:
Ism Yuborish
Kamida 3 harf
Maydon?Xato ko'rsatildi — lekin "Al" yo'qoldi. React action tugaganini ko'rdi va formani tozaladi. U natija "muvaffaqiyat"mi yoki "xato"mi — bilmaydi. Uzun izoh yozgan mehmon hammasini qaytadan yozishi kerak. Malika uchun bu yanada og'ir: ekran o'quvchi bilan maydonni qayta topish va yozish.
5.2 Yechim: qiymatni state orqali qaytarish
Action yozilgan qiymatni ham qaytarsin, maydon esa uni defaultValue sifatida olsin:
import { useActionState } from "react";
function check(prevState, formData) {
const name = formData.get("name");
if (name.length < 3) return { error: "Kamida 3 harf", name };
return { error: null, name: "" };
}
function App() {
const [state, formAction] = useActionState(check, {
error: null,
name: "",
});
function showField() {
const field = document.querySelector("[name=name]");
console.log("Maydonda:", JSON.stringify(field.value));
}
return (
<>
<form action={formAction}>
<label>
Ism <input name="name" defaultValue={state.name} />
</label>
<button>Yuborish</button>
<p>{state.error ?? "—"}</p>
</form>
<button onClick={showField}>Maydon?</button>
</>
);
}Konsolda:
Maydonda: "Al"Ekranda:
Ism Yuborish
Kamida 3 harf
Maydon?Tozalash baribir bo'ldi, lekin maydon "boshlang'ich qiymat" ga qaytadi — defaultValue esa endi state.name, ya'ni "Al". Bu ishlaydi, lekin har maydonni state'da qaytarish kerak — uncontrolled formaning soddaligi yo'qola boshlaydi.
5.3 form.reset() va defaultValue
Action'siz formada tozalashning eski yo'li — form.reset(). U maydonlarni boshlang'ich qiymatga qaytaradi. React defaultValue ni HTML value atributiga yozadi — brauzer uchun aynan u "boshlang'ich qiymat". Agar defaultValue bo'sh bo'lmasa:
function App() {
function handleSubmit(e) {
e.preventDefault();
e.currentTarget.reset(); // "qo'shildi — tozalaymiz"
}
function showField() {
const field = document.querySelector("[name=matn]");
console.log("Maydonda:", JSON.stringify(field.value));
}
return (
<>
<form onSubmit={handleSubmit}>
<label>
Vazifa <input name="matn" defaultValue="Qoralama matni" />
</label>
<button>Qo'shish</button>
</form>
<button onClick={showField}>Maydon?</button>
</>
);
}Konsolda:
Maydonda: "Qoralama matni"reset() maydonni bo'shatmadi — defaultValue ga qaytardi. Bu HTML'ning to'g'ri xulqi, lekin "tozaladim" deb o'ylagan dasturchi uchun kutilmagan.
6. Nega vazifalar <form action> ni ishlatmaydi
6.1 O'lchov
vazifalar dagi yangi vazifa formasining ikki xususiyati bor:
- Maydonning
defaultValuesi — qoralama: sahifa yangilansa, yozilayotgan matn tiklanadi (16-qismda qo'shilgan; matn yozish to'xtagach saqlanadi). - Tekshiruv xato topishi mumkin (bo'sh matn, takror vazifa, 200 belgidan uzun). Xato bo'lsa matn maydonda qolishi kerak — tuzatish uchun.
Kanonni yozishda ikkala yo'lni sinab ko'rdik. Birinchisi — <form action>, action esa "tekshiruv xato topdi" deb hech narsa qilmaydi. Haqiqiy Vitest natijasi (React 19.3). Chiqish uzun, lekin faqat ikki qatorga qarang: - bilan boshlangani — test kutgan qiymat, + bilan boshlangani — aslida olingan qiymat.
AssertionError: expected { actionKordi: [ 'non OLISH' ], …(1) } to deeply equal { actionKordi: [ 'non OLISH' ], …(1) }
- Expected
+ Received
{
"actionKordi": [
"non OLISH",
],
- "maydonda": "non OLISH",
+ "maydonda": "",
}"Kutilgan: maydonda non OLISH qoladi. Olingan: bo'sh satr." Action matnni ko'rdi, lekin forma tozalandi — «Xato bo'lsa ham» bo'limidagi tuzoq.
Ikkinchisi — onSubmit va muvaffaqiyatda form.reset(). Bu yerda ham - va + qatorlarini solishtiring:
AssertionError: expected [ 'Qoralama matni', 'Qoralama matni' ] to deeply equal [ 'Qoralama matni', '' ]
- Expected
+ Received
[
"Qoralama matni",
- "",
+ "Qoralama matni",
]Birinchi element — value atributi (React defaultValue ni yozgan), ikkinchisi — reset() dan keyingi qiymat. Vazifa qo'shildi, lekin maydonda eski qoralama qaytib keldi.
6.2 Kanondagi yechim
YangiVazifaForma.tsx (kanondan, qisqartirilgan):
// Uncontrolled + FormData (17/#19): qiymat har harfda emas, yuborilganda
// kerak — formadan name="matn" bo'yicha o'qiladi. <form action={fn}>
// emas: React action'dan keyin formani O'ZI tozalaydi — tekshiruv
// xato topsa ham, va matn yo'qoladi (o'lchandi, 19-form-action.txt)
function yuborildi(event: FormEvent<HTMLFormElement>) {
event.preventDefault(); // sahifa qayta yuklanmasin
const maydon = maydonniOl(event.currentTarget);
const matn = new FormData(event.currentTarget).get("matn");
if (maydon === null || typeof matn !== "string") {
return;
}
const natija = onQosh(matn.trim());
switch (natija.tur) {
case "xato":
setXato(natija.xabar);
maydon.focus();
return;
case "saqlanmadi":
return; // e'lonni asosiy.ts qildi, matn maydonda qoladi
case "qoshildi":
setXato(null);
setTakliflar([]);
// form.reset() EMAS: React defaultValue ni value atributiga
// yozadi va reset() qoralamani qaytarardi (o'lchandi)
maydon.value = "";
return;onSubmit+preventDefault— forma faqat biz xohlaganda tozalanadi.FormData(...).get("matn")— turiFormDataEntryValue | null(satr yokiFile), shuning uchuntypeof matn !== "string"tekshiruvi (Narrowing).- Xato bo'lsa — matn joyida, fokus maydonga.
- Muvaffaqiyatda —
maydon.value = "": aynan bitta maydonni bo'shatish,reset()emas.
Form action — yaxshi vosita, lekin u "har yuborishdan keyin forma yangidan boshlanadi" degan farazga qurilgan. Bizning formada bu faraz to'g'ri emas. Action'lar server bilan ishlaganda (Next.js Server Actions, 23-qism) ayniqsa qulay. U yerda xato holatida qiymatni qaytarish — «Yechim: qiymatni state orqali qaytarish» bo'limidagi usul. React'da formani qo'lda tozalatish uchun react-dom dagi requestFormReset(form) ham bor — Actions darsida.
flowchart TD
S["Forma yuborildi"] --> Q{"Qiymat har harfda kerakmi?"}
Q -- "ha: tekshiruv, format" --> C["Controlled: value + onChange"]
Q -- "yo'q" --> X{"Xatoda matn qolishi kerakmi?"}
X -- "yo'q yoki serverda" --> A["form action + useActionState"]
X -- "ha, oddiy forma" --> F["onSubmit + FormData"]7. Vazifalar qadami: FormData, eksport va import React'da
7.1 Yangi vazifa — FormData
Oldingi darsda ochilgan refactor/formalar branch'ida davom etamiz. YangiVazifaForma dagi maydon uncontrolled qoladi, lekin qiymat endi FormData dan:
- const matn = kiritma.value.trim(); // tahrirMaydoni() — DOM'dan
+ const [matn, setMatn] = useState(vazifa.matn); // TahrirQatori state'i
+ <input value={matn} onChange={(event) => setMatn(event.target.value)} … />
+ const matn = new FormData(event.currentTarget).get("matn"); // yangi vazifa, importBu — bitta fayl diff'i emas, uch fayldagi asosiy qatorlarning qisqa xulosasi. Unda ikki formalar darsi birga: controlled tahrir (Formalar I) va FormData (bugun).
7.2 Import — uncontrolled textarea
Eksport va import hali index.html da vanilla edi. Endi ular komponent. Import.tsx dagi forma (kanondan):
// FormData (17/#19): formadagi name="paket" maydon qiymati
function matndan(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const matn = new FormData(event.currentTarget).get("paket");
importQil(typeof matn === "string" ? matn : "", "matn");
}
…
<form onSubmit={matndan}>
<label htmlFor="import-matn">Eksport qilingan JSON matni</label>
<textarea
id="import-matn"
name="paket"
rows={6}
aria-invalid={xato?.manba === "matn" ? true : undefined}
/>Import matni bir necha kilobayt bo'lishi mumkin — har harfda state yangilash ma'nosiz. Fayl maydoni (<input type="file">) esa React'da har doim uncontrolled: xavfsizlik sababli uning qiymatini JavaScript yoza olmaydi (faqat bo'shatish mumkin). Kanonda shunday:
// O'sha faylni qayta tanlash ham change hodisasini bersin
event.currentTarget.value = "";Sudrab tashlashda fayl zona ustidami — ustida state'i, className esa undan hisoblanadi. Brauzer tekshiruvida (e2e ssenariy — sahifani foydalanuvchi kabi bosib chiqadigan skript) bitta o'zgarish shu yerdan chiqdi. Endi class="ustida" React state'idan keladi va hodisa tugagach chiziladi — ssenariy uni o'qishdan oldin 50 ms kutadi. 159 tekshiruvning soni va matni o'zgarmadi.
7.3 Eksport — controlled, lekin faqat o'qish uchun
<textarea id="eksport-matn" rows={6} readOnly value={json} />value + readOnly — Formalar I dagi ogohlantirishning uchinchi yo'li: maydon faqat ko'rsatadi. JSON matni useState da, tugma bosilganda yangilanadi.
7.4 Tekshiruv va commit
npm run build && npm test && npm run lint && npm run tipKanon natijalari: node:test — 149, Vitest — 22 (yangi EksportImport.test.tsx), brauzer — 159/159. Diff (holat 16 → 19, ikki dars birga): 12 fayl, +337 −165. index.html dan eksport va import bloklari olib tashlandi — App ga <Eksport /> va <Import /> qo'shildi.
refactor: controlled tahrir, FormData, eksport/import React'daBody: "TahrirQatori — useState(vazifa.matn) (fon yangilanganda matn state'da qoladi; fondaChiz qoralama tiklashi olib tashlandi). Yangi vazifa va import — FormData. Eksport, Import komponentlari (drag — ustida state)." PR'ni birlashtiring.
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
maydon FormData da yo'q |
name atributi yo'q |
name qo'shing |
| sahifa qayta yuklanadi | onSubmit da preventDefault yo'q |
e.preventDefault() yoki action={fn} |
| checkbox'lardan bittasi keldi | Object.fromEntries oxirgisini oladi |
getAll("nom") |
"7" + 1 = "71" |
FormData qiymati — satr |
Number() |
| action'dan keyin xato bo'lsa ham matn yo'qoldi | React formani o'zi tozalaydi | qiymatni state'da qaytaring yoki onSubmit |
reset() dan keyin eski matn |
reset() defaultValue ga qaytaradi |
kerakli maydonni value = "" |
defaultValue o'zgardi, maydon o'zgarmadi |
u faqat boshlang'ich qiymat | key bilan qayta yarating (State'ning saqlanishi) |
9. Mashqlar
Mashqlar kurs/mashqlar/17/19-uncontrolled-formalar/ da.
1-mashq (oson): FormData natijasi
Formada: <input name="name"> ("Ali"), ikkita <input type="checkbox" name="extra"> (value="non" belgilangan, value="choy" belgilanmagan), <input name="note" disabled> ("izoh").
data.get("name"):data.getAll("extra")dagi elementlar soni:data.get("note"):
Yechim
get("name") — "Ali". Belgilanmagan checkbox kirmaydi — getAll("extra") → ["non"], bitta. disabled maydon ham FormData ga kirmaydi — get("note") → null.
2-mashq (o'rta): "Fikr bildiring" formasi
Uncontrolled forma: ism (required), baho (select: 1–5, sukut bo'yicha 5), izoh (textarea). onSubmit + FormData. Yuborilgach pastda: "Ali: — Juda mazali" (baho nechta bo'lsa, shuncha yulduz; izoh bo'sh bo'lsa — faqat "Ali: "). Baho son bo'lsin (Number), ★ — baho soniga teng marta takrorlansin ("★".repeat(n), satr metodlari). Hech qanday maydon state'ga bog'lanmasin — state faqat natija uchun.
Yechim
import { useState } from "react";
function App() {
const [review, setReview] = useState(null);
function handleSubmit(e) {
e.preventDefault();
const data = new FormData(e.currentTarget);
const stars = Number(data.get("stars"));
const note = data.get("note").trim();
const text = `${data.get("name")}: ${"★".repeat(stars)}`;
setReview(note === "" ? text : `${text} — ${note}`);
}
return (
<form onSubmit={handleSubmit}>
<label>
Ism <input name="name" required />
</label>
<select name="stars" defaultValue="5" aria-label="Baho">
{[1, 2, 3, 4, 5].map((n) => (
<option key={n} value={n}>
{n}
</option>
))}
</select>
<label>
Izoh <textarea name="note" />
</label>
<button>Yuborish</button>
{review && <p>{review}</p>}
</form>
);
}Ekranda:
Ism 12345Izoh Yuborish
Ali: ★★★★★ — Zo'rMaydonlar uncontrolled — yozish paytida render yo'q. select ning defaultValue="5", <option selected> emas. Bu safar textarea ga yozilgan matn ekran natijasida yo'q: React unga tegmagan. Oldingi darsdagi controlled textarea da esa React har o'zgarishda matnni element ichiga ham yozadi — shuning uchun u ko'ringan edi.
3-mashq (qiyin): Bron — useActionState bilan, matn yo'qolmasin
useActionState bilan bron formasi: ism va mehmonlar soni. Action (async, 200 ms "server"): ism bo'sh bo'lsa — "Ism kerak"; soni 1–12 oralig'ida bo'lmasa — "1 dan 12 gacha". Xato bo'lsa, ikkala maydondagi qiymat yo'qolmasin («Yechim: qiymatni state orqali qaytarish» bo'limidagi usul). Muvaffaqiyatda: "Malika, 4 kishi — bron qilindi" va forma bo'sh. Kutish paytida tugma bloklangan.
Yechim
import { useActionState } from "react";
const EMPTY = { error: null, done: null, name: "", guests: "" };
async function book(prev, formData) {
await new Promise((r) => setTimeout(r, 200));
const name = formData.get("name").trim();
const guests = formData.get("guests");
const n = Number(guests);
const values = { done: null, name, guests };
if (name === "") return { ...values, error: "Ism kerak" };
if (!Number.isInteger(n) || n < 1 || n > 12) {
return { ...values, error: "1 dan 12 gacha" };
}
return { ...EMPTY, done: `${name}, ${n} kishi — bron qilindi` };
}
function App() {
const [state, formAction, isPending] = useActionState(book, EMPTY);
return (
<form action={formAction}>
<label>
Ism <input name="name" defaultValue={state.name} />
</label>
<label>
Soni{" "}
<input
type="number"
name="guests"
defaultValue={state.guests}
/>
</label>
<button disabled={isPending}>Bron</button>
<p role="status">
{isPending
? "Yuborilmoqda…"
: (state.error ?? state.done ?? "")}
</p>
</form>
);
}Ekranda:
Ism Soni Bron
1 dan 12 gachaXato bo'ldi, lekin "Malika" va "0" maydonlarda qoladi: React formani tozaladi, maydonlar esa yangi defaultValue (state.name, state.guests) ga qaytdi. "0" ni "4" ga tuzatib, qayta yuborsangiz — "Malika, 4 kishi — bron qilindi" va maydonlar bo'sh (EMPTY). Tekshiruv uchun alohida test: shu harakat oxirida maydon qiymatini konsolga chiqarib ko'ring.
4-mashq: Vazifalar qadami
refactor/formalar branch'ida «Vazifalar qadami» dagi o'zgarishlarni kiriting: YangiVazifaForma.tsx (FormData), Import.tsx, Eksport.tsx (yangi), App.tsx ga ikkalasi, index.html dan eksport/import bloklari, asosiy.ts dagi importQil(matn): string | null. Keyin tekshiruvlar, commit va PR.
Yechim
npm run tip:get("matn")ning turiFormDataEntryValue | null—typeof … !== "string"tekshiruvisiz.trim()TS2339beradi (Filedatrimyo'q).- Brauzerda: bo'sh vazifa qo'shing — xato chiqadi, maydonga yozilgan bo'shliqlar ham joyida. To'g'ri vazifa — maydon bo'sh, sahifani yangilang — qoralama ham bo'sh.
- Import'ga buzuq JSON tashlang — xato matni chiqadi, textarea'dagi matn qoladi (uncontrolled, React unga tegmaydi).
npm test— node 149, Vitest 22.
Yakuniy kod — refactor: controlled tahrir, FormData, eksport/import React'da commit'i.
10. Real ishda
- Ko'p formalar uncontrolled — login, ro'yxatdan o'tish, "biz bilan bog'laning". React Hook Form (RHF I) aynan uncontrolled maydonlar va
nameustiga qurilgan — shuning uchun tez. - Form action'lar — Next.js va React Server Components dunyosida asosiy yo'l (23-qism):
actionserverdagi funksiya bo'lishi mumkin. Xatoda qiymatni qaytarish — o'sha yerda ham kerak bo'ladigan usul. - Progressive enhancement: oddiy HTML forma JavaScript yuklanmasdan ham ishlaydi. Server actions bilan React formasi ham shunday ishlay oladi — uncontrolled +
namebunga asos. - Intervyu savollari: "Uncontrolled input qachon afzal?", "
FormDatadan obyekt qanday olinadi va nimasi yo'qoladi?", "React 19 form action nima qiladi?", "useActionStatenimani qaytaradi?".
Xulosa
- Uncontrolled maydon: qiymat DOM'da, React faqat
defaultValue/defaultCheckedberadi;name— manzil. new FormData(form)— butun forma;Object.fromEntries— obyekt,getAll— bir nomlilar; hammasi satr.- Brauzer validatsiyasi ishlaydi; o'z xabaringiz uchun
noValidate+validity. Server tekshiruvi baribir shart. <form action={fn}>—FormDatani o'zi beradi;useActionState—[state, formAction, isPending].- Action'dan keyin React formani tozalaydi, xatoda ham;
reset()esadefaultValuega qaytaradi — ikkalasi o'lchangan. vazifalar: yangi vazifa va import —onSubmit+FormData, tahrir — controlled, eksport —readOnly.
Keyingi dars: React DevTools bilan ishlash — komponent daraxti, props, state va hooklarni brauzerda ko'rish va o'zgartirish.
Manbalar
- React:
<form>(action) — react.dev/reference/react-dom/components/form - React:
useActionState— react.dev/reference/react/useActionState - React blog: "React v19" (2024-12-05) — Actions,
useActionState— react.dev/blog - MDN:
FormData,ValidityState— developer.mozilla.org
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!