Mundarija (28)
- Bu darsda
- 1. Nega bu kerak?
- 2. useActionState chuqur
- 2.1 Imzo — qisqa takror
- 2.2 prevState: natijani to'plash
- 2.3 Navbat: Action'lar ketma-ket
- 2.4 Formasiz: dispatch tugmadan
- 2.5 Xato: qaytaring, tashlamang
- 2.6 permalink — server framework'lar uchun
- 3. useFormStatus
- 3.1 Tugma o'z formasini biladi
- 3.2 Faqat forma ichida
- 3.3 Qaysi biri qachon
- 4. Vazifalar qadami: yangi vazifa formasi — <form action> (2-qism)
- 4.1 17-qismdagi qaror
- 4.2 Action natijasi: FormaHolati
- 4.3 Tugma — useFormStatus
- 4.4 Xato tuzatilsa — yo'qolsin
- 4.5 Test va brauzer
- 5. Ko'p uchraydigan xatolar
- 6. Mashqlar
- 1-mashq (oson): qaysi vosita?
- 2-mashq (o'rta): qayta ishlatiladigan tugma
- 3-mashq (qiyin): stol bron qilish — natija va navbat
- 4-mashq: Vazifalar qadami
- 7. Real ishda
- Xulosa
- Manbalar
useActionState va useFormStatus: Action natijasi, navbat va forma holati props'siz
Qisqacha:
useActionState(action, initialState)— Action'ning natijasini state qiladi:[state, dispatch, isPending]. Action(prevState, payload)ni oladi va yangi state'ni qaytaradi. Bir necha yuborish bo'lsa, React ularni navbat bilan bajaradi. Keyingisi oldingisining natijasiniprevStatesifatida oladi.dispatchni<form action>ga bering yokistartTransitionichida chaqiring.useFormStatus()(react-domdan) — forma ichidagi komponentga o'z formasining holatini beradi:pending,data,method,action. Props kerak emas. Lekin formani chizgan komponentning o'zida u doimpending: false.
Bu darsda
prevStatebilan natijani to'plab boradigan Action yozasiz (savat).- Action'lar navbatini konsolda ko'rasiz va nega bu oddiy transition'dan xavfsizroq ekanini tushuntirasiz.
dispatchni formasiz, tugmadan chaqirasiz va React'ning ogohlantirishini o'qiysiz.useFormStatusbilan qayta ishlatiladigan yuborish tugmasini yozasiz va "faqat forma ichida" qoidasini jonli ko'rasiz.vazifalardagi yangi vazifa formasini<form action>ga ko'chirasiz: xato bo'lsa matn joyida, tugma "Qo'shilmoqda…".
Oldin bilishingiz kerak: Actions, Formalar II: form action, Context I, useReducer.
1. Nega bu kerak?
Oldingi darsda Action'ni ko'rdik: kutish holatini React o'zi biladi, forma o'zi tozalanadi. Sardor bron formasini Action'ga aylantirdi va ikki savolga duch keldi.
Birinchisi — natija. Action "5-stol bron qilindi" yoki "bu stol band" deb javob berishi kerak. <form action> esa qaytgan qiymatni e'tiborsiz qoldiradi. Sardor yana useState va try/catch ga qaytdi.
Ikkinchisi — tugma. Dizayner «Bahor» uchun chiroyli SubmitButton komponentini chizdi: yuborilayotganda aylanadigan belgi va "Yuborilmoqda…" yozuvi bilan. U saytdagi 12 ta formada ishlatiladi. Har formada isPending ni olib, tugmaga prop qilib berish kerak bo'ldi. 12 joy — 12 ta imkoniyat unutishga.
Hayotiy o'xshatish: oshxonadagi buyurtma navbati. Ofitsiant buyurtmalarni oshpazga bittadan beradi. Oshpaz har yangi buyurtmani oldingisi tugagan stolning joriy holatiga qarab tayyorlaydi: "stolga 2 osh berilgan edi, endi yana 1 — jami 3". Bu — useActionState. Zaldagi tablo esa har stolga "buyurtmangiz tayyorlanmoqda" deb ko'rsatadi. Stol egasi oshpazdan so'rab yurmaydi — tablo o'zi biladi. Bu — useFormStatus.
2. useActionState chuqur
2.1 Imzo — qisqa takror
Formalar II darsida ko'rgansiz:
const [state, dispatch, isPending] = useActionState(
action, // (prevState, payload) => yangi state (async ham)
initialState, // birinchi renderdagi state
permalink, // ixtiyoriy — server framework'lar uchun
);state— Action oxirgi marta qaytargan qiymat. Hali chaqirilmagan bo'lsa —initialState.dispatch— o'ralgan Action. Uni<form action>ga yokiformActionga bering, yokistartTransitionichida chaqiring. React hujjatlari uni ko'pinchaformActiondeb ataydi — ikkalasi bir narsa.isPending— Action (yoki navbatdagilardan biri) bajarilayotganmi.
Bugun uchta yangi narsa: prevState nima uchun kerak, navbat va permalink.
2.2 prevState: natijani to'plash
Action birinchi argument sifatida oldingi natijani oladi. Shuning uchun u reducer ga juda o'xshaydi: (holat, amal) => yangi holat. Farqi — Action async bo'la oladi va serverga borib kela oladi.
«Bahor» savati: har «Savatga» bosilganda taom serverga yoziladi va ro'yxatga qo'shiladi:
import { useActionState } from "react";
async function addToCart(prevCart, formData) {
await new Promise((resolve) => setTimeout(resolve, 100));
const dish = formData.get("dish").trim();
if (dish === "") return prevCart; // o'zgarishsiz
return [...prevCart, dish];
}
export default function App() {
const [cart, formAction, isPending] = useActionState(addToCart, []);
return (
<form action={formAction}>
<label>
Taom <input name="dish" />
</label>
<button disabled={isPending}>Savatga</button>
<p>Savatda: {cart.length > 0 ? cart.join(", ") : "bo'sh"}</p>
</form>
);
}Ekranda:
Taom Savatga
Savatda: Osh, MantiKomponentda useState yo'q. Savat — Action natijasi, Action esa uni prevCart dan davom ettiradi. Bo'sh nom kelsa — prevCart o'zgarishsiz qaytadi: state o'sha massiv, ekran o'zgarmaydi.
2.3 Navbat: Action'lar ketma-ket
Oldingi darsda ikki marta bosilgan oddiy Action'lar parallel ishladi — ikkita so'rov bir vaqtda yo'lda edi. useActionState boshqacha. Ikki marta tez bosamiz:
import { useActionState } from "react";
let started = 0;
async function addDish(prevCount) {
const id = ++started;
console.log(`${id}-so'rov boshlandi, oldingi: ${prevCount}`);
await new Promise((resolve) => setTimeout(resolve, 200));
console.log(`${id}-so'rov tugadi`);
return prevCount + 1;
}
export default function App() {
const [count, formAction, isPending] = useActionState(addDish, 0);
console.log("render:", count, isPending);
return (
<form action={formAction}>
<button>+1 osh</button>
<p>Buyurtmada: {count} osh</p>
</form>
);
}Konsolda:
render: 0 false
1-so'rov boshlandi, oldingi: 0
render: 0 true
render: 0 true
1-so'rov tugadi
2-so'rov boshlandi, oldingi: 1
2-so'rov tugadi
render: 2 falseEkranda:
+1 osh
Buyurtmada: 2 oshKonsolni o'qiymiz:
| Qadam | Nima bo'ldi |
|---|---|
| 1-bosish | 1-so'rov darhol boshlandi, isPending — true |
| 2-bosish | 2-so'rov boshlanmadi — navbatda turibdi |
| 1-so'rov tugadi | natija 1 — lekin ekranga chiqmadi |
| 2-so'rov boshlandi | prevCount — 1: oldingisining natijasi |
| 2-so'rov tugadi | bitta render: 2, isPending: false |
Ikki xulosa. Birinchisi: 2-so'rov 1-sining natijasini ko'rdi. Agar ular parallel bo'lganda, ikkalasi ham prevCount: 0 ni olib, 1 qaytarardi — bitta osh yo'qolardi. Ikkinchisi: oraliq natija (1) ekranga chiqmadi — navbat tugaguncha React kutdi. Oldingi darsdagi "Action yangilanishlari birga chiziladi" qoidasi navbatga ham tegishli.
Navbatning narxi — tezlik: 10 ta bosish = 10 ta ketma-ket so'rov. Ko'pincha bu to'g'ri tanlov: server tartibni to'g'ri ko'radi. Tezlik kerak bo'lsa — keyingi darsdagi useOptimistic: ekran darhol, so'rovlar fonda.
Tekshirib ko'ring: Ikkinchi
render: 0 trueqatori qayerdan chiqdi? Hech narsa o'zgarmagan-ku.
Javob
Birinchi render: 0 true — bosish paytidagi shoshilinch render: React faqat isPending ni yangiladi. Ikkinchisi — transition renderi: React Action'ni boshladi, lekin u await da kutyapti. Actions darsidagi "Saqlanmoqda…" misolida ham xuddi shu to'rt qator bor edi. Ikkinchi bosish yangi render chaqirmadi — u navbatga yozildi, xolos.
2.4 Formasiz: dispatch tugmadan
dispatch ga FormData berish shart emas. Ikkinchi argument — istalgan payload: id, obyekt, satr. Savatni tugmalar bilan to'ldiramiz:
import { useActionState } from "react";
async function addToCart(prevCart, dish) {
await new Promise((resolve) => setTimeout(resolve, 100));
return [...prevCart, dish];
}
export default function App() {
const [cart, dispatch] = useActionState(addToCart, []);
return (
<div>
<button onClick={() => dispatch("Osh")}>Osh</button>
<p>Savatda: {cart.join(", ")}</p>
</div>
);
}Konsolda:
An async function with useActionState was called outside of a transition. This is likely not what you intended (for example, isPending will not update correctly). Either call the returned function inside startTransition, or pass it to an `action` or `formAction` prop.Ekranda:
Osh
Savatda: OshSavat ishladi, lekin React ogohlantirdi: "useActionState dagi async funksiya transition'dan tashqarida chaqirildi. Ehtimol siz buni xohlamagansiz (masalan, isPending to'g'ri yangilanmaydi). Qaytgan funksiyani startTransition ichida chaqiring yoki action yoki formAction prop'iga bering."
<form action> da React transition'ni o'zi ochadi. Oddiy onClick da esa — siz:
import { startTransition, useActionState } from "react";
async function addToCart(prevCart, dish) {
await new Promise((resolve) => setTimeout(resolve, 100));
return [...prevCart, dish];
}
export default function App() {
const [cart, dispatch, isPending] = useActionState(addToCart, []);
function handleClick() {
startTransition(() => dispatch("Osh")); // ✅
}
return (
<div>
<button onClick={handleClick}>Osh</button>
<p>
{isPending ? "Qo'shilmoqda…" : `Savatda: ${cart.join(", ")}`}
</p>
</div>
);
}Ekranda:
Osh
Savatda: OshOgohlantirish yo'q, isPending esa kutish paytida "Qo'shilmoqda…" ni ko'rsatdi (ekran bosishdan 60 ms keyin tekshirdi).
2.5 Xato: qaytaring, tashlamang
Action throw qilsa — Actions darsidagidek, eng yaqin error boundary'ga ketadi. Bundan tashqari, navbatda turgan keyingi yuborishlar ham bekor qilinadi: React hujjati (react.dev, useActionState, 2026-10) shunday deydi. Buni o'zimiz tekshiramiz: uch marta tez bosamiz, birinchi so'rov esa xato bilan tugaydi. Konsolda nechta "boshlandi" qatori chiqishiga qarang:
import { useActionState } from "react";
let started = 0;
async function addDish(prevCount) {
const id = ++started;
console.log(`${id}-so'rov boshlandi`);
await new Promise((resolve) => setTimeout(resolve, 200));
if (id === 1) throw new Error("500: server xatosi");
return prevCount + 1;
}
export default function App() {
const [count, formAction] = useActionState(addDish, 0);
return (
<form action={formAction}>
<button>+1 osh</button>
<p>Buyurtmada: {count} osh</p>
</form>
);
}Konsolda:
1-so'rov boshlandi
Uncaught Error: 500: server xatosiNima bo'ldi? Uchta bosish bo'ldi, lekin faqat bitta so'rov boshlandi. 2- va 3-yuborish navbatda kutib turgan edi. 1-so'rov throw qilgach, ular umuman boshlanmadi — xato bilan birga yo'qoldi. Error boundary bo'lmagani uchun ilova ham o'chdi. Foydalanuvchi uchun bu ikki marta yomon: xato ekrani va yo'qolgan ikki buyurtma.
Shuning uchun kutilgan xatolarni state'ning bir qismi qilib qaytaring:
import { useActionState } from "react";
async function book(prevState, formData) {
await new Promise((resolve) => setTimeout(resolve, 100));
const name = formData.get("name").trim();
if (name.length < 3) {
return { error: "Ism kamida 3 harf", name }; // yozilgani qaytadi
}
const done = `${name}, stol bron qilindi`;
return { error: null, name: "", done };
}
const INITIAL = { error: null, name: "", done: null };
export default function App() {
const [state, formAction] = useActionState(book, INITIAL);
return (
<form action={formAction}>
<label>
Ism <input name="name" defaultValue={state.name} />
</label>
<button>Bron qilish</button>
{state.error && <p role="alert">{state.error}</p>}
{state.done && <p>{state.done}</p>}
</form>
);
}Ekranda:
Ism Bron qilish
Ism kamida 3 harfBu Formalar II dagi usul: defaultValue={state.name}. React Action'dan keyin formani reset qiladi, maydon esa "Al" ga qaytadi — xato bilan qaytgan natijadagi qiymatga. Muvaffaqiyatda name: "" — maydon bo'shaydi. role="alert" — Malika xatoni darhol eshitadi.
2.6 permalink — server framework'lar uchun
Uchinchi argument — URL satri. U faqat server komponentlar bilan ishlaydigan framework'da (Next.js) ma'noga ega. Sahifa serverdan HTML bo'lib keldi, JavaScript esa hali yuklanmagan. Foydalanuvchi formani shu paytda yuborsa, brauzer uni permalink manziliga oddiy HTML forma kabi jo'natadi. Server javobni o'sha sahifa bilan qaytaradi.
// Next.js: JS yuklanmasdan oldin ham forma ishlaydi
const [state, formAction] = useActionState(
bookTable,
INITIAL,
"/bron",
);Bunday "JS'siz ham ishlash" progressive enhancement (bosqichma-bosqich yaxshilash) deyiladi. Bizning Vite ilovamizda server komponent yo'q — permalink hech narsa qilmaydi. To'liq — Server Actions II darsida.
3. useFormStatus
3.1 Tugma o'z formasini biladi
useFormStatus — react-dom dagi hook. U ota <form> ning hozirgi yuborilish holatini qaytaradi. Dizaynerning tugmasi:
import { useFormStatus } from "react-dom";
function SubmitButton() {
const { pending, data, method } = useFormStatus();
console.log("SubmitButton:", pending, method, data?.get("name"));
const name = data?.get("name");
return (
<button disabled={pending}>
{pending ? `${name} uchun yuborilmoqda…` : "Yuborish"}
</button>
);
}
async function book(formData) {
await new Promise((resolve) => setTimeout(resolve, 200));
}
export default function App() {
return (
<form action={book}>
<label>
Ism <input name="name" />
</label>
<SubmitButton />
</form>
);
}Konsolda:
SubmitButton: false null undefined
SubmitButton: true get Malika
SubmitButton: false null undefinedEkranda:
Ism YuborishSubmitButton hech qanday prop olmadi. Lekin u bildi: forma yuborilyapti (pending), qaysi ma'lumot bilan (data — o'sha FormData) va qaysi usulda (method). Bu book oddiy forma Action'i — useActionState siz ham ishladi. 200 ms dan keyin hammasi boshlang'ich holatga qaytdi.
useFormStatus() qaytaradigan obyekt:
| Maydon | Yuborilmaganda | Yuborilayotganda |
|---|---|---|
pending |
false |
true |
data |
null |
yuborilgan FormData |
method |
null |
"get" yoki "post" (forma atributidan) |
action |
null |
formaning action i (funksiya yoki URL) |
method nega "get"? Formaga method atributi yozilmagan, HTML'da esa standart — GET. React funksiya-Action'ni baribir o'zi chaqiradi, method faqat ma'lumot uchun.
Ichkarida bu Context ga o'xshaydi: <form> holatini "ta'minlaydi", ichidagi komponentlar uni o'qiydi. Farqi — Provider ni siz yozmaysiz, uni <form> ning o'zi beradi.
3.2 Faqat forma ichida
Eng ko'p uchraydigan tuzoq: hook'ni formani chizgan komponentning o'zida chaqirish:
import { useFormStatus } from "react-dom";
async function book(formData) {
await new Promise((resolve) => setTimeout(resolve, 200));
}
export default function App() {
// ❌ forma — pastda, App ichida emas
const { pending } = useFormStatus();
console.log("App pending:", pending);
return (
<form action={book}>
<button disabled={pending}>
{pending ? "Yuborilmoqda…" : "Yuborish"}
</button>
</form>
);
}Konsolda:
App pending: falseBir marta chizildi — va tamom. Forma yuborildi, book 200 ms ishladi, lekin App qayta chizilmadi ham. useFormStatus yuqoriga qaraydi: chaqirilgan komponentning ota-bobolari ichidan <form> qidiradi. App esa formaning ichida emas — u formani yaratadi. Ota forma yo'q — holat doim "yuborilmagan".
Tuzatish — tugmani alohida komponentga ajratish («Tugma o'z formasini biladi» bo'limidagidek). Shuning uchun useFormStatus deyarli doim kichik "bola" komponentda yashaydi.
flowchart TD
A["App: useFormStatus — ota forma yo'q"] --> F["form action={book}"]
F --> I["input"]
F --> B["SubmitButton: useFormStatus — ota forma bor"]
F -. "holat" .-> BDiagrammada uzuq chiziqqa qarang: forma holati faqat pastga, o'z ichidagilarga uzatiladi. App undan yuqorida turibdi.
Tekshirib ko'ring: Sahifada ikki forma bor, ikkalasida ham
<SubmitButton />. Birinchi forma yuborilyapti. Ikkinchi formadagi tugmadapendingqanday?
Javob
false. Har SubmitButton faqat o'z ota formasini ko'radi. Context kabi: eng yaqin ta'minlovchi — o'z formasi. Shuning uchun bitta SubmitButton komponentini 12 ta formada xavfsiz ishlatish mumkin — ular bir-birining holatini ko'rmaydi.
3.3 Qaysi biri qachon
isPending endi uch joydan keladi. Tanlov — qayerda kerakligiga qarab:
| Vosita | Nimani biladi | Qayerda ishlatiladi |
|---|---|---|
useTransition |
o'zi boshlagan transition'lar | startTransition chaqirgan komponentda |
useActionState |
shu Action'ning natijasi va navbati | formani chizgan komponentda |
useFormStatus |
ota formaning yuborilishi | forma ichidagi bola komponentda |
Ko'pincha ikkalasi birga: forma komponenti — useActionState (natija, xato), uning ichidagi tugma — useFormStatus (kutish). vazifalar formasi aynan shunday.
4. Vazifalar qadami: yangi vazifa formasi — <form action> (2-qism)
4.1 17-qismdagi qaror
Formalar II darsida vazifalar formasi <form action> emas, onSubmit bilan yozilgan edi. Sabab o'lchangan: React Action'dan keyin formani tozalardi va xato bo'lsa ham matn yo'qolardi. Endi bizda ikki yangi narsa bor: useActionState natijasidagi matn va useFormStatus. Shu ikkalasi bilan qaror qayta ko'rildi — <form action> kanonga kirdi.
4.2 Action natijasi: FormaHolati
YangiVazifaForma.tsx — kanondan aynan (izohlar telefonga sig'ishi uchun qatorlarga qayta bo'lingan):
// Action natijasi (useActionState state'i): xato va maydonda
// QOLADIGAN matn. React action tugagach formani O'ZI tozalaydi
// (reset — maydon defaultValue'ga qaytadi); defaultValue — shu matn:
// xato bo'lsa yozilgan matn joyida qoladi, qo'shilsa — bo'sh.
// 17/#19 da aynan shu muammo (reset matnni o'chirardi)
// <form action> ni kanondan chiqargan edi
type FormaHolati = {
readonly xato: string | null;
readonly matn: string;
};Va Action'ning o'zi:
// useActionState (18/#06): action natijasi (oldingi state →
// yangisi). Bir necha yuborish — navbat bilan (biri tugagach
// keyingisi)
const [holat, yuborish] = useActionState(
async (
_oldingi: FormaHolati,
malumot: FormData,
): Promise<FormaHolati> => {
const xom = malumot.get("matn");
const yozilgan = typeof xom === "string" ? xom : "";
const natija = await onQosh(yozilgan.trim());
switch (natija.tur) {
case "xato":
return { xato: natija.xabar, matn: yozilgan };
case "saqlanmadi":
// internet yo'q yoki server rad etdi — matn maydonda
// qoladi
return { xato: null, matn: yozilgan };
case "qoshildi":
setTakliflar([]);
return { xato: null, matn: "" };
default: {
const qolmadi: never = natija;
return qolmadi;
}
}
},
{ xato: null, matn: boshlangichMatn },
);Har qismni o'qiymiz:
_oldingi—prevState, lekin bu formaga kerak emas: har yuborish mustaqil. Pastki chiziq — "ataylab ishlatilmaydi" belgisi (ESLint uni kechiradi).await onQosh(...)— App'dagivazifaniQoshendiasync: server javobigacha kutadi (useOptimistic da ko'ramiz). Forma shu paytgacha "kutilmoqda".- Uch natija — uch state. Xato bo'lsa va saqlanmasa —
matn: yozilgan, qo'shilsa —matn: "".never— discriminated union dagi to'liqlik tekshiruvi: yangiturqo'shilsa,tscshu yerda xato beradi. - Boshlang'ich state —
{ xato: null, matn: boshlangichMatn }: sahifa yangilanganda qoralama tiklanadi (16-qismdan).
Maydon esa natijadagi matnni oladi:
- defaultValue={boshlangichMatn}
+ defaultValue={holat.matn}onSubmit + preventDefault + maydon.value = "" o'rniga — <form id="yangi-forma" noValidate action={yuborish}>.
4.3 Tugma — useFormStatus
// useFormStatus (18/#06): o'z formasining holati — props'siz.
// Faqat <form> ICHIDAGI komponentda ishlaydi (forma o'zi emas).
// Tugma bloklanmaydi (optimistik: keyingi vazifani yozish mumkin),
// faqat matni
function QoshishTugmasi() {
const { pending } = useFormStatus();
return (
<button type="submit" id="qoshish-tugma">
{pending ? "Qo'shilmoqda…" : "Qo'shish"}
</button>
);
}E'tibor bering: disabled yo'q. Bron formasida ikki marta bosishdan himoya kerak edi. Bu yerda esa vazifa ekranda darhol paydo bo'ladi (optimistik — keyingi dars) va foydalanuvchi keyingisini yozishi mumkin. Navbat esa ularni server tartibida yuboradi.
4.4 Xato tuzatilsa — yo'qolsin
Eski kodda xato useState edi — tuzatilsa setXato(null). Endi xato — Action natijasining bir qismi, uni to'g'ridan-to'g'ri o'zgartirib bo'lmaydi. Kanondagi yechim — qaysi natijaning xatosi "tuzatilgan"ini eslash:
// Xato tuzatilsa — yo'qolsin: qaysi natijaning xatosi
// "tuzatilgan"
const [tuzatilgan, setTuzatilgan] = useState<FormaHolati | null>(
null,
);
const xato = tuzatilgan === holat ? null : holat.xato;Matn o'zgarib, tekshiruvdan o'tsa — setTuzatilgan(holat). Keyingi yuborish yangi holat obyektini beradi va u tuzatilgan ga teng emas — yangi xato yana ko'rinadi. xato — hosilaviy qiymat, alohida state emas.
Fokus ham o'zgardi. Eski kodda xato bo'lsa maydon.focus() hodisa ichida chaqirilardi. Endi natija Action tugagach keladi — hodisa allaqachon o'tib ketgan. Shuning uchun fokus — effektda:
// Xato bilan qaytgan natija — fokus maydonga («Qo'shish»
// bosilgan bo'lsa ham). Natija action tugagach keladi — hodisada
// emas
useLayoutEffect(() => {
if (holat.xato !== null) {
maydonRef.current?.focus();
}
}, [holat]);useLayoutEffect — bo'yashdan oldin: Bobur fokus sakrashini ko'rmaydi.
4.5 Test va brauzer
tekshiruv/YangiVazifaForma.test.tsx ga ikki test qo'shildi. Xato bilan qaytsa — React formani tozaladi, lekin maydonda non OLISH qoldi (maydon.defaultValue ham shu). Server kutilayotganda tugma Qo'shilmoqda…, matn hali maydonda; javobdan keyin — Qo'shish va bo'sh maydon. Action async bo'lgani uchun yuborish await act(async () => { fireEvent.submit(...) }) ichida — natija Action tugagach chiziladi.
Haqiqiy Chrome'da (e2e-18, soxta server, kechikish va 500):
✅ forma: kutilmoqda — tugma (useFormStatus), matn hali maydonda: ["Qo'shilmoqda…","Guruch olish"]
✅ forma: tasdiqlandi — maydon tozalandi (React reset), id serverdan (100): ["",true]
✅ forma: rad etildi — vazifa yo'q, matn maydonda qoldi (useActionState): [false,"Yo'qolmaydigan matn"]17-qismda <form action> ni chiqargan muammo — "rad etildi, matn yo'qoldi" — endi yo'q. Commit — useOptimistic darsida.
5. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
useFormStatus doim pending: false |
formani chizgan komponentda chaqirilgan | tugmani alohida komponentga ajrating |
An async function with useActionState was called outside of a transition |
dispatch oddiy onClick da |
startTransition(() => dispatch(...)) |
| ikkinchi yuborish "kech" boshlanadi | useActionState navbati |
kutilgan xulq; tezlik kerak bo'lsa — useOptimistic |
prevState ishlatilmasa ham bor |
imzo (prevState, payload) |
birinchi argumentni _ bilan boshlang |
| xato bo'lsa maydon bo'shaydi | natijada yozilgan qiymat yo'q | { error, name } qaytaring, defaultValue={state.name} |
| xatodan keyin navbatdagi yuborishlar yo'qoldi | Action throw qildi — navbat bekor |
kutilgan xatoni qaytaring, throw — faqat kutilmagani uchun |
data.get(...) — Cannot read properties of null |
pending: false paytida data — null |
data ni faqat pending da o'qing yoki data?.get |
6. Mashqlar
Mashqlar kurs/mashqlar/18/06-useactionstate/ da. Har yechimni «Ishga tushir» bilan sinang.
1-mashq (oson): qaysi vosita?
Har holat uchun useActionState, useFormStatus yoki useTransition deb yozing.
- Forma ichidagi umumiy
SubmitButtonkutish paytida aylanadigan belgi ko'rsatsin. - Server "bu stol band" deb javob bersa, forma ostida shu matn chiqsin.
- Formasiz «Yangilash» tugmasi ro'yxatni serverdan qayta olsin va kutish paytida tugma xira bo'lsin.
Yechim
- useFormStatus — tugma forma ichida, holatni props'siz o'qiydi («Tugma o'z formasini biladi» bo'limi).
- useActionState — Action natijasi (xato matni) state bo'ladi va ekranda chiziladi.
- useTransition — forma yo'q, natija ham state'da emas; faqat "kutilmoqda" kerak (Actions).
2-mashq (o'rta): qayta ishlatiladigan tugma
Boshlang'ich kodda "Yuborilmoqda…" hech qachon ko'rinmaydi. SubmitButton komponentini yozing: useFormStatus bilan kutish paytida disabled va "Yuborilmoqda…", aks holda "Yuborish". Uni App dagi tugma o'rniga qo'ying. Ishora: «Faqat forma ichida» bo'limi.
import { useFormStatus } from "react-dom";
async function sendReview(formData) {
await new Promise((resolve) => setTimeout(resolve, 200));
}
export default function App() {
const { pending } = useFormStatus();
return (
<form action={sendReview}>
<label>
Sharh <input name="review" />
</label>
<button disabled={pending}>
{pending ? "Yuborilmoqda…" : "Yuborish"}
</button>
</form>
);
}Ekranda:
Sharh YuborishYechim
import { useFormStatus } from "react-dom";
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button disabled={pending}>
{pending ? "Yuborilmoqda…" : "Yuborish"}
</button>
);
}
async function sendReview(formData) {
await new Promise((resolve) => setTimeout(resolve, 200));
}
export default function App() {
return (
<form action={sendReview}>
<label>
Sharh <input name="review" />
</label>
<SubmitButton />
</form>
);
}Ekranda:
Sharh Yuborishekran("Yuborilmoqda…") bosilgandan 60 ms keyin tekshiradi — SubmitButton forma ichida, pending ni ko'rdi. 200 ms dan keyin — yana "Yuborish". App dan useFormStatus olib tashlandi: u yerda hech narsa bermasdi.
3-mashq (qiyin): stol bron qilish — natija va navbat
useActionState bilan bron formasi: mehmonlar soni (name="guests"). Action (100 ms "server"):
- Son 1–12 oralig'ida bo'lmasa —
role="alert"da "1 dan 12 gacha", yozilgan son maydonda qolsin. - To'g'ri bo'lsa — jami bron qilingan o'rinlar oldingi natijaga qo'shilsin: "Bron qilingan o'rinlar: 4", keyin yana 2 — "6". Maydon bo'shasin.
- Tugma — alohida
BookButton(useFormStatus): kutish paytida "Bron qilinmoqda…".
Ishora: state — { total, error, guests }; defaultValue={state.guests}; «prevState: natijani to'plash» va «Xato: qaytaring, tashlamang» bo'limlari.
Yechim
import { useActionState } from "react";
import { useFormStatus } from "react-dom";
async function book(prev, formData) {
await new Promise((resolve) => setTimeout(resolve, 100));
const raw = formData.get("guests");
const guests = Number(raw);
if (!Number.isInteger(guests) || guests < 1 || guests > 12) {
return { ...prev, error: "1 dan 12 gacha", guests: raw };
}
return { total: prev.total + guests, error: null, guests: "" };
}
function BookButton() {
const { pending } = useFormStatus();
return (
<button>{pending ? "Bron qilinmoqda…" : "Bron qilish"}</button>
);
}
const INITIAL = { total: 0, error: null, guests: "" };
export default function App() {
const [state, formAction] = useActionState(book, INITIAL);
return (
<form action={formAction}>
<label>
Mehmonlar{" "}
<input name="guests" defaultValue={state.guests} />
</label>
<BookButton />
<p>Bron qilingan o'rinlar: {state.total}</p>
{state.error && <p role="alert">{state.error}</p>}
</form>
);
}Ekranda:
Mehmonlar Bron qilish
Bron qilingan o'rinlar: 4
1 dan 12 gachaBirinchi yuborish: 4 — to'g'ri, total 0 + 4. Ikkinchisi: 20 — xato, ...prev bilan total o'zgarmay qoldi, guests: "20" maydonga qaytdi. Endi 2 yozib yuborsangiz — 6 va xato yo'qoladi. prev bo'lmaganda total ni alohida useState da saqlash va ikki state'ni moslab turish kerak bo'lardi.
4-mashq: Vazifalar qadami
refactor/actions-optimistik branch'ida YangiVazifaForma.tsx ni «Vazifalar qadami» bo'limi bo'yicha o'zgartiring: FormaHolati, useActionState, QoshishTugmasi, defaultValue={holat.matn}, tuzatilgan va fokus effekti. onQosh turini Promise<QoshishNatijasi> ga o'zgartiring. Testlarni await act ga o'tkazing.
Yechim
Tekshiruv ro'yxati:
<form ... action={yuborish}>—onSubmitvapreventDefaultyo'q.- Vitest: yangi ikki test (xato — matn joyida,
useFormStatus— "Qo'shilmoqda…") o'tadi. Kanonda bu bosqichda Vitest 33 ta. - Qo'lda:
npm run devda takror vazifani yozing — xato chiqadi, matn maydonda, fokus maydonda. - Commit hali yo'q — useOptimistic darsida, App bilan birga.
7. Real ishda
- Next.js Server Actions —
useActionState(serverAction, initialState)— eng ko'p uchraydigan juftlik: validatsiya xatolari serverdan state bo'lib qaytadi (Server Actions II).permalinkva progressive enhancement aynan shu yerda ishlaydi. - Dizayn tizimlari —
SubmitButton,FormSpinnerkabi komponentlaruseFormStatusbilan yoziladi: har formaga prop uzatish kerak emas. - React Hook Form — katta formalarda (o'nlab maydon, maydon darajasidagi xatolar) ko'pincha shu kutubxona; u ham React 19 Action'lari bilan ishlay oladi (React Hook Form I).
- Intervyu savollari: "
useActionStatenimani qaytaradi vaprevStatenima uchun?", "negauseFormStatusformani chizgan komponentda ishlamaydi?", "bir necha yuborish bo'lsa nima bo'ladi?".
Xulosa
useActionState(action, initial)→[state, dispatch, isPending]; Action —(prevState, payload) => yangi state.- Yuborishlar navbat bilan: keyingisi oldingisining natijasini oladi, oraliq natijalar chizilmaydi.
dispatch—<form action>,formActionyokistartTransitionichida; aks holda ogohlantirish.- Kutilgan xato — state'da qaytariladi (
defaultValuebilan matn saqlanadi);throw— navbatni bekor qiladi. useFormStatus—pending,data,method,action; faqat forma ichidagi komponentda.vazifalar: forma —<form action>+useActionState({ xato, matn })+QoshishTugmasi(2-qism, commit — #07).
Keyingi dars: useOptimistic — server javobini kutmasdan natijani ko'rsatish va rad etilsa React o'zi qaytarishi.
Manbalar
- React:
useActionState— react.dev/reference/react/useActionState (2026-10 holati, React 19.3) - React:
useFormStatus— react.dev/reference/react-dom/hooks/useFormStatus - React blog: "React v19" (2024-12-05) —
useActionState,useFormStatus— react.dev/blog/2024/12/05/react-19 - MDN:
FormData— developer.mozilla.org/docs/Web/API/FormData
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!