Mundarija (22)
- Bu darsda
- 1. Nega bu kerak?
- 2. Mashina — jadval
- 2.1 Jonli model: o'tishlar jadvali
- 2.2 Model nimani bilmaydi
- 3. XState 5: statechart
- 3.1 setup, context, assign, guard va invoke
- 3.2 Kechiktirilgan o'tish va parallel holatlar
- 4. React'da: useMachine
- 4.1 Bosqichli forma
- 4.2 @xstate/store — mashinasiz kichik store
- 4.3 Zag va vizualizator
- 5. Kanon tajribasi: XState varianti (nega kanonga kirmadi)
- 6. Ko'p uchraydigan xatolar
- 7. Mashqlar
- 1-mashq (oson): jadvalni o'qing
- 2-mashq (o'rta): stol broni guard bilan
- 3-mashq (qiyin): kutubxonasiz after
- 4-mashq (tahlil): mashina kerakmi?
- 8. Real ishda
- Xulosa
- Manbalar
XState holat mashinalari: statechart, guard, invoke va useMachine bilan murakkab oqimlar
Qisqacha: Holat mashinasi — tizim bir vaqtda faqat bitta aniq holatda bo'ladigan va holatdan holatga faqat ruxsat etilgan hodisa bilan o'tadigan model: "imkonsiz holat" (bir vaqtda "yuborilmoqda" va "xato") yo'q. XState 5 uni statechart darajasiga ko'taradi:
context(ma'lumot), guard (shartli o'tish),after(taymer), ichma-ich va parallel holatlar,invoke(promise yoki boshqa aktor). React'dauseMachine(mashina)→[snapshot, send]. Kichik "store" uchun —@xstate/store, UI vidjetlari uchun — Zag. Kanon tajribasida XState +12.9 KB va 42 qator: oddiy ro'yxatga ortiqcha, murakkab oqimda — o'rinli.
Bu darsda
- Jadval asosidagi kichik mashinani kutubxonasiz jonli yozasiz va "rad etilgan hodisa" ni ko'rasiz.
- XState 5 da
setup+createMachinebilancontext,assign, guard vainvokeli to'lov oqimini qurasiz. - Kechiktirilgan o'tish (
after), ichma-ich va parallel holatlarni ishlatasiz. - React'da
useMachine,snapshot.can()vasnapshot.matches()bilan bosqichli formani boshqarasiz. @xstate/store, Zag va Stately vizualizatorini taniysiz.- Holat mashinasi qachon kerak va qachon ortiqcha ekanini kanon tajribasi bilan ayta olasiz.
Oldin bilishingiz kerak: Reducer + Immer va holat mashinasi fikrlashi, Atom va proxy state, useReducer, Redux Toolkit II.
1. Nega bu kerak?
Reducer + Immer va holat mashinasi fikrlashi darsida bron formasini boolean'lardan bitta status ga o'tkazgan edik va XState bilan qisqa tanishgan edik. Endi «Bahor» saytida haqiqiy to'lov oqimi kerak:
- Savat bo'sh bo'lsa, "To'lash" ishlamasin.
- To'lov serverga ketadi; javob kutilayotganda tugma qayta bosilmasin.
- Karta rad etilsa — xato va «Qayta urinish», lekin ko'pi bilan 2 marta.
- Shu vaqtda oshxona buyurtmani tayyorlay boshlaydi — to'lovni kutmasdan. 5 daqiqa to'lanmasa — kassirga eslatma.
Sardor buni useState lar bilan yozdi: isPaying, hasError, retries, isCooking, isPaid, reminderTimer. Olti o'zgaruvchi — 2⁵ × taymer holatlari, ularning ko'pchiligi ma'nosiz ("to'lanmoqda" va "to'landi" bir vaqtda). Har yangi talab yangi if qo'shadi va eski if lar bilan to'qnashadi.
Hayotiy o'xshatish: metro turniketi. Unda bor-yo'g'i ikki holat — "yopiq" va "ochiq". Yopiqda karta tekkizsangiz — ochiladi; itarsangiz — hech narsa. Ochiqda o'tsangiz — yopiladi. Turniket "yarim ochiq" bo'lmaydi, ikkita karta tekkizish ikki kishini o'tkazmaydi. Qoidalar jadvali hamma holatni oldindan aytadi — holat mashinasi shu.
2. Mashina — jadval
2.1 Jonli model: o'tishlar jadvali
Chekli holat mashinasi (finite state machine) uch narsadan iborat: holatlar ro'yxati, boshlang'ich holat va o'tishlar: "shu holatda shu hodisa kelsa — shu holatga". Jadvalda yo'q juftlik — rad etilgan hodisa: holat o'zgarmaydi. Saytda XState yo'q, shuning uchun avval kutubxonasiz o'quv modeli — mashina oddiy obyekt, o'tish esa useReducer:
import { useReducer } from "react";
// O'quv modeli: o'tishlar jadvali (XState emas)
const machine = {
initial: "bosh",
states: {
bosh: { ADD: "savatda" },
savatda: { ADD: "savatda", PAY: "tolanmoqda" },
tolanmoqda: { OK: "tolandi", FAIL: "xato" },
xato: { PAY: "tolanmoqda" },
tolandi: {},
},
};
function transition(state, event) {
const next = machine.states[state][event.type];
if (next === undefined) {
console.log(`rad etildi: ${state} + ${event.type}`);
return state;
}
console.log(`${state} → ${next}`);
return next;
}
export default function App() {
const [state, send] = useReducer(transition, machine.initial);
return (
<>
<p>Holat: {state}</p>
<button onClick={() => send({ type: "ADD" })}>Osh</button>{" "}
<button onClick={() => send({ type: "PAY" })}>To'lash</button>
</>
);
}Konsolda:
rad etildi: bosh + PAY
bosh → savatda
savatda → tolanmoqda
rad etildi: tolanmoqda + ADDEkranda:
Holat: tolanmoqda
Osh To'lashIkki if yozmadik: "savat bo'sh bo'lsa to'lama" va "to'lov ketayotganda savatni o'zgartirma". Ikkalasi ham jadvalning o'zidan kelib chiqdi — bosh da PAY yo'q, tolanmoqda da ADD yo'q. Mashina tilida xato — "imkonsiz holat" emas, shunchaki "bu hodisa bu yerda ma'nosiz".
stateDiagram-v2
[*] --> bosh
bosh --> savatda: ADD
savatda --> savatda: ADD
savatda --> tolanmoqda: PAY
tolanmoqda --> tolandi: OK
tolanmoqda --> xato: FAIL
xato --> tolanmoqda: PAY
tolandi --> [*]Diagramma jadvalning o'zi: har strelka — bitta yozuv. Strelka yo'q joyda hodisa rad etiladi. Bunday rasmni jadvaldan avtomatik chizish mumkin — «Vizualizator» bo'limida.
2.2 Model nimani bilmaydi
Jadval chiroyli, lekin talablarimizning yarmini qoplamaydi:
- Ma'lumot. Savatdagi taomlar, urinishlar soni — holat nomi emas. Ularni qayerda saqlaymiz?
- Shart. "Qayta urinish — faqat 2 martagacha": bir xil hodisa, bir xil holat, lekin ba'zan ruxsat, ba'zan yo'q.
- Vaqt. "5 daqiqada to'lanmasa — eslatma": hodisani foydalanuvchi emas, vaqt yuboradi.
- Async ish. To'lov so'rovi: holatga kirganda boshlanadi, chiqqanda bekor bo'lishi kerak.
- Bir vaqtda ikki jarayon. Oshxona va to'lov bir-biridan mustaqil. Bitta jadvalda ularni birlashtirsak, holatlar ko'paytiriladi: 3 × 3 = 9 ta.
Bularning hammasini qo'lda qo'shish mumkin — yoki bularni o'z ichiga olgan statechart dan foydalanish mumkin.
Tekshirib ko'ring: Modelda
xatoholatidaADDyuborilsa nima bo'ladi? Bu to'g'rimi?
Javob
Rad etiladi: xato: { PAY: "tolanmoqda" } da ADD yo'q. To'g'rimi — biznes qarori. Mehmon to'lov rad etilgach savatni o'zgartirmoqchi bo'lishi tabiiy. Unda jadvalga ADD: "savatda" qo'shasiz. Mashinaning foydasi aynan shu: bu savol kod ichida yashirinmaydi, jadvalda ochiq turadi va uni Jasur aka bilan muhokama qilsa bo'ladi.
3. XState 5: statechart
3.1 setup, context, assign, guard va invoke
Statechart — 1987 yilda David Harel taklif qilgan kengaytirilgan holat mashinasi: kontekst (ma'lumot), shartli o'tishlar, ichma-ich va parallel holatlar, vaqt. XState — uni JavaScript'da amalga oshiradigan kutubxona. Kursda versiyalari: xstate 5.33.2, @xstate/react 6.1.0 (kanon tajribasi). XState 5 da mashina setup({...}).createMachine({...}) bilan yoziladi: setup ichida mashinada ishlatiladigan guard, amal va aktorlar nom bilan e'lon qilinadi.
«Nega bu kerak?» bo'limidagi 1–3 talablar bitta mashinada. Saytda XState yo'q — Node'da ishga tushiramiz:
import { assign, createActor, fromPromise, setup } from "xstate";
// soxta to'lov serveri: 1-urinish rad, 2-si muvaffaqiyat
let attempt = 0;
const payOnServer = fromPromise(async ({ input }) => {
attempt += 1;
await new Promise((r) => setTimeout(r, 20));
if (attempt === 1) throw new Error("Karta rad etildi");
return `chek-${input.total}`;
});
const checkoutMachine = setup({
actors: { payOnServer },
guards: {
hasItems: ({ context }) => context.items.length > 0,
canRetry: ({ context }) => context.retries < 2,
},
}).createMachine({
id: "buyurtma",
initial: "savat",
context: { items: [], retries: 0, receipt: null },
states: {
savat: {
on: {
ADD: {
actions: assign({
items: ({ context, event }) => [
...context.items,
event.dish,
],
}),
},
CHECKOUT: { guard: "hasItems", target: "tolov" },
},
},
tolov: {
invoke: {
src: "payOnServer",
input: ({ context }) => ({
total: context.items.length * 1000,
}),
onDone: {
target: "tasdiqlandi",
actions: assign({ receipt: ({ event }) => event.output }),
},
onError: { target: "xato" },
},
},
xato: {
on: {
RETRY: {
guard: "canRetry",
target: "tolov",
actions: assign({
retries: ({ context }) => context.retries + 1,
}),
},
},
},
tasdiqlandi: { type: "final" },
},
});
const actor = createActor(checkoutMachine);
actor.subscribe((s) => console.log("holat:", s.value));
actor.start();
actor.send({ type: "CHECKOUT" }); // savat bo'sh — guard rad etadi
actor.send({ type: "ADD", dish: "Osh" });
actor.send({ type: "CHECKOUT" });
await new Promise((r) => setTimeout(r, 50));
actor.send({ type: "RETRY" });
await new Promise((r) => setTimeout(r, 50));
const snap = actor.getSnapshot();
console.log(snap.status, snap.context.receipt, snap.context.retries);Toza papkada xstate@5.33.2 bilan Node 24 da:
holat: savat
holat: savat
holat: savat
holat: tolov
holat: xato
holat: tolov
holat: tasdiqlandi
done chek-1000 1Qatorma-qator:
context— mashinaning ma'lumoti:items,retries,receipt. Holat nomi (savat,tolov) "qayerdamiz" ni, kontekst "nima bor" ni aytadi.assign— kontekstni yangilaydigan amal (action). U sof: eski kontekstdan yangisini hisoblaydi, reducer kabi.- Guard — o'tishga shart.
CHECKOUT: { guard: "hasItems", target: "tolov" }: savat bo'sh — guardfalse— o'tish yo'q. Konsoldagi ikkinchiholat: savat— rad etilganCHECKOUT(kuzatuvchi har hodisadan keyin snapshot oladi, holat o'sha). Uchinchisi —ADD(holat o'sha, kontekst yangi). invoke— holatga kirganda ishga tushadigan va undan chiqqanda to'xtatiladigan aktor.fromPromise— promise'dan aktor. Natija —onDone(event.output), xato —onError.tolovholatidan chiqildimi — so'rov "kerak emas" deb hisoblanadi: race condition ning oldi olinadi.- Urinishlar: birinchi to'lov rad etildi →
xato.RETRY—canRetry(retries < 2) — ruxsat,retries1 bo'ldi, qaytatolov. Ikkinchi urinish o'tdi →tasdiqlandi,type: "final"→ aktorstatus: done.
Aktor (actor) — XState'ning asosiy birligi: o'z holatiga ega, xabar (hodisa) oladi va boshqa aktorlarni yarata oladi. createActor(mashina) — mashinadan jonli nusxa; mashinaning o'zi — faqat tavsif (Jotai'dagi atom va store kabi).
3.2 Kechiktirilgan o'tish va parallel holatlar
To'rtinchi talab: oshxona va to'lov bir vaqtda, va to'lov kechiksa — eslatma. Parallel holat (type: "parallel") — bir vaqtda faol bo'lgan bir nechta mustaqil "hudud". Kechiktirilgan o'tish (after: { 100: "eslatma" }) — holatda shuncha ms qolinsa, o'tish o'zi bo'ladi; holatdan oldinroq chiqilsa, taymer bekor qilinadi. Misolda 5 daqiqa o'rniga 100 ms:
import { createActor, createMachine } from "xstate";
// buyurtma: oshxona va to'lov — PARALLEL, bir-birini kutmaydi
const orderMachine = createMachine({
id: "buyurtma",
type: "parallel",
states: {
oshxona: {
initial: "tayyorlanmoqda",
states: {
tayyorlanmoqda: { on: { READY: "tayyor" } },
tayyor: { type: "final" },
},
},
tolov: {
initial: "kutilmoqda",
states: {
// 100 ms da to'lanmasa — eslatma (kechiktirilgan o'tish)
kutilmoqda: {
on: { PAID: "tolandi" },
after: { 100: "eslatma" },
},
eslatma: { on: { PAID: "tolandi" } },
tolandi: { type: "final" },
},
},
},
});
const actor = createActor(orderMachine);
actor.subscribe((s) => console.log(JSON.stringify(s.value)));
actor.start();
actor.send({ type: "READY" });
await new Promise((r) => setTimeout(r, 150));
actor.send({ type: "PAID" });
console.log("status:", actor.getSnapshot().status);Node 24, xstate 5.33.2:
{"oshxona":"tayyorlanmoqda","tolov":"kutilmoqda"}
{"oshxona":"tayyor","tolov":"kutilmoqda"}
{"oshxona":"tayyor","tolov":"eslatma"}
{"tolov":"tolandi","oshxona":"tayyor"}
status: done- Holat qiymati endi obyekt: har hudud — o'z holati. Bir paytning o'zida
oshxona: tayyorvatolov: kutilmoqda. READYfaqat oshxona hududini o'zgartirdi, to'lovga tegmadi.- 100 ms o'tdi —
eslatma. Hech kim hodisa yubormadi: vaqtning o'zi hodisa. - Ikkala hudud ham
finalga yetgach — butun mashinadone.
Ichma-ich (hierarchical) holat — holat ichida holatlar: misoldagi oshxona va tolov ning har biri initial va states ga ega. Ota holatdagi on hamma bolalarga tegishli bo'ladi: masalan, tolov: { on: { CANCEL: "#buyurtma.bekor" }, states: {...} } — to'lovning qaysi bosqichida bo'lmasin, bekor qilish ishlaydi. Bitta qoida, uch joyga takrorlanmaydi.
stateDiagram-v2
state buyurtma {
state oshxona {
[*] --> tayyorlanmoqda
tayyorlanmoqda --> tayyor: READY
}
--
state tolov {
[*] --> kutilmoqda
kutilmoqda --> eslatma: 100 ms
kutilmoqda --> tolandi: PAID
eslatma --> tolandi: PAID
}
}Diagrammadagi uzuq chiziq (--) — parallel hududlar chegarasi. Bitta jadvalda yozilsa, 2 × 3 = 6 holat va 10 dan ortiq strelka bo'lardi; parallel holat bilan — ikki mustaqil kichik mashina.
Tekshirib ko'ring: Parallel misolda
PAID50 ms da (eslatmadan oldin) yuborilsa, konsoldaeslatmachiqadimi?
Javob
Yo'q. PAID kutilmoqda dan tolandi ga o'tkazadi — holatdan chiqildi, after taymeri bekor qilinadi. Kechiktirilgan o'tish faqat holatda uzluksiz shuncha vaqt qolinganda ishlaydi. Qo'lda setTimeout bilan yozilganda buni unutish oson: taymer o'chirilmay qolsa, to'langan buyurtmaga eslatma ketadi.
4. React'da: useMachine
4.1 Bosqichli forma
React'da mashina useMachine bilan ulanadi: u aktorni komponent bilan birga yaratadi va to'xtatadi, [snapshot, send] qaytaradi — useReducer ga juda o'xshash. Stol bron qilishning uch qadamli formasi (@xstate/react 6.1.0, React 19.3.0, Chrome 155 — «Keyingi», «Orqaga», «Keyingi», «Keyingi», «Tasdiqlash» bosildi):
import { useMachine } from "@xstate/react";
import { createMachine } from "xstate";
const wizardMachine = createMachine({
id: "bron",
initial: "sana",
states: {
sana: { on: { NEXT: "mehmonlar" } },
mehmonlar: { on: { NEXT: "tasdiq", BACK: "sana" } },
tasdiq: { on: { CONFIRM: "tayyor", BACK: "mehmonlar" } },
tayyor: { type: "final" },
},
});
export default function App() {
const [snapshot, send] = useMachine(wizardMachine);
console.log("qadam:", snapshot.value);
return (
<>
<p>Qadam: {snapshot.value}</p>
<button
disabled={!snapshot.can({ type: "BACK" })}
onClick={() => send({ type: "BACK" })}
>
Orqaga
</button>{" "}
<button
disabled={!snapshot.can({ type: "NEXT" })}
onClick={() => send({ type: "NEXT" })}
>
Keyingi
</button>{" "}
{snapshot.matches("tasdiq") && (
<button onClick={() => send({ type: "CONFIRM" })}>
Tasdiqlash
</button>
)}
</>
);
}qadam: sana
qadam: mehmonlar
qadam: sana
qadam: mehmonlar
qadam: tasdiq
qadam: tayyorIkki foydali metod:
snapshot.can(hodisa)— "bu hodisa hozir qabul qilinadimi?" Tugmaningdisabledholati mashinadan olinadi:sanada «Orqaga» o'chiq,tasdiqda «Keyingi» o'chiq. Shart ikki joyda (mashina va JSX) yozilmaydi.snapshot.matches(holat)— "hozir shu holatdamizmi?" (ichma-ich holatda ham:matches({ tolov: "eslatma" })).
Oxirgi holat tayyor — final: hech bir hodisa qabul qilinmaydi, ikkala tugma o'chiq.
Ikkita boshqa hook: useActor (tayyor aktor bilan) va useSelector(aktor, selektor) — aktor snapshot'idan faqat bir bo'lakni o'qish (Zustand I dagi selektor g'oyasi). Global mashina kerak bo'lsa (butun ilova uchun bitta), createActor modul darajasida yaratiladi va komponentlar useSelector bilan o'qiydi.
4.2 @xstate/store — mashinasiz kichik store
Har holat ham mashina emas. Savat — oddiy "ma'lumot + amallar". XState jamoasining @xstate/store paketi aynan shu uchun: Zustand'ga o'xshash kichik store, lekin hodisalar (send({ type })) bilan va XState vositalari bilan mos. Versiya — 4.2.3 (2026-08-10; 4.3.0 bir haftadan yangiroq bo'lgani uchun olinmadi):
import { createStore } from "@xstate/store";
const cartStore = createStore({
context: { items: [], total: 0 },
on: {
added: (context, event) => ({
items: [...context.items, event.name],
total: context.total + event.price,
}),
cleared: () => ({ items: [], total: 0 }),
},
});
cartStore.subscribe((snapshot) => {
const { items, total } = snapshot.context;
console.log(items, total);
});
cartStore.send({ type: "added", name: "Osh", price: 35000 });
cartStore.trigger.added({ name: "Choy", price: 5000 });
cartStore.trigger.cleared();Node 24, @xstate/store 4.2.3:
[ 'Osh' ] 35000
[ 'Osh', 'Choy' ] 40000
[] 0on dagi har funksiya — hodisa nomi va yangi kontekstni qaytaradigan "kichik reducer". cartStore.trigger.added({...}) — send({ type: "added", ... }) ning qisqa yozuvi (TypeScript'da hodisa turlari avtomatik). React'da — useSelector(cartStore, (s) => s.context.total); 4-versiyada u alohida paketda: @xstate/store-react (README bo'yicha o'rnatish — npm install @xstate/store-react, useSelector shu paketdan import qilinadi; 2.x versiyalari @xstate/store 4 ga tayanadi). Eski 3-versiyada u @xstate/store/react yo'lida edi — eski maqolalarda shuni ko'rasiz. Holat keyinchalik murakkablashsa (holatlar, guard'lar paydo bo'lsa), uni to'liq mashinaga ko'chirish oson — hodisa nomlari o'sha.
4.3 Zag va vizualizator
Zag (@zag-js/*) — holat mashinalari asosidagi UI vidjetlari: menyu, dialog, combobox, slayder, sana tanlagich. Ichida — har vidjetning tayyor mashinasi (ochiq/yopiq, fokus qayerda, klaviatura nima qiladi, ARIA atributlari). Siz faqat ko'rinishni chizasiz. Ark UI va Chakra UI 3 shu asosda qurilgan — UI kutubxonalar xaritasi da ko'ramiz. Headless komponentlar darsidagi "mantiq alohida, ko'rinish alohida" g'oyasining mashina bilan qilingani.
Vizualizator — Stately (XState mualliflari) ning stately.ai/viz sahifasi va VS Code kengaytmasi: mashina kodini joylashtirasiz, u diagramma chizadi va siz hodisalarni bosib, oqimni "o'ynatib" ko'rasiz. Teskarisi ham bor: diagrammani chizib, koddan generatsiya qilish. Biz ishlatgan mermaid stateDiagram — xuddi shu g'oyaning statik varianti.
Diqqat: vizualizatorga kod joylashtirish — uni uchinchi tomon saytiga yuborish. Ish loyihasida maxfiy mantiq bo'lsa, kompaniya qoidasini so'rang yoki VS Code kengaytmasining oflayn rejimidan foydalaning.
5. Kanon tajribasi: XState varianti (nega kanonga kirmadi)
Bu darsda vazifalar o'zgarmaydi. Tajriba — bir xil vazifa (ro'yxat: qo'shish, belgilash, filtr, "qoldi") yetti holat vositasi bilan (Redux Toolkit I va Jotai, Valtio, MobX darslaridagi grafiklar). XState varianti (aynan):
// holat-xstate.tsx — XState 5 + @xstate/react 6 (mashina:
// holatlar va hodisalar; bu oddiy ro'yxat uchun — bitta holat,
// context'da ma'lumot)
import { assign, setup } from "xstate";
import { useMachine } from "@xstate/react";
import { Korinish, ishgaTushir } from "./_umumiy.tsx";
import type { Filtr, Vazifa } from "./_umumiy.tsx";
const mashina = setup({
types: {
context: {} as { vazifalar: Vazifa[]; filtr: Filtr },
events: {} as
| { type: "qosh"; matn: string }
| { type: "almashtir"; id: number }
| { type: "filtr"; filtr: Filtr },
},
}).createMachine({
context: { vazifalar: [], filtr: "hammasi" },
on: {
qosh: {
actions: assign({
vazifalar: ({ context, event }) => [...context.vazifalar, { id: context.vazifalar.length + 1, matn: event.matn, bajarildi: false }],
}),
},
almashtir: {
actions: assign({
vazifalar: ({ context, event }) => context.vazifalar.map((v) => (v.id === event.id ? { ...v, bajarildi: !v.bajarildi } : v)),
}),
},
filtr: { actions: assign({ filtr: ({ event }) => event.filtr }) },
},
});
function Ilova() {
const [h, send] = useMachine(mashina);
return (
<Korinish
holat={{
...h.context,
qosh: (matn) => send({ type: "qosh", matn }),
almashtir: (id) => send({ type: "almashtir", id }),
filtrla: (filtr) => send({ type: "filtr", filtr }),
}}
/>
);
}
ishgaTushir(<Ilova />);Natija: +12.9 KB gzip (eng og'ir) va 42 qator (eng uzun). Faylning o'z izohi aytganidek: "bu oddiy ro'yxat uchun — bitta holat, context'da ma'lumot". Mashinaning kuchi — holatlar va o'tishlar — bu vazifada umuman ishlatilmadi: states yo'q, faqat on + assign. Ya'ni XState bu yerda qimmat reducer bo'ldi.
Nega kanonga kirmadi: vazifalar dagi eng murakkab oqim — ko'p qadamli forma (/yangi, RHF III: wizard). U React Hook Form'ning trigger(maydonlar) va oddiy qadam raqami bilan yetarli bo'ldi: qadamlar chiziqli (oldinga/orqaga), parallel jarayon va taymer yo'q. O'chirish dialogi, tarmoq holati, Orqaga tugmasi — har biri 2–3 holatli va o'z joyida (holat mashinasi fikrlashi bilan status maydonlari).
Qachon XState — to'g'ri tanlov:
- Ko'p bosqichli tarmoqlanuvchi oqim: to'lov, KYC (shaxsni tasdiqlash), bron, onboarding — guard'lar, qayta urinish, bekor qilish.
- Vaqt muhim: taymerlar, "N soniyada javob bo'lmasa", qayta ulanish (WebSocket — Real-time).
- Parallel jarayonlar: yuklash + tahrir + avtosaqlash; media pleyer (ijro, ovoz, to'liq ekran).
- Holatlar biznes bilan muhokama qilinadi: diagramma — umumiy til, test esa har o'tishni tekshiradi.
6. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| hodisa yuborildi, hech narsa bo'lmadi | joriy holatda bu hodisa yo'q (rad etildi) yoki guard false |
snapshot.can(hodisa); vizualizatorda tekshiring |
context o'zgarmadi |
context.items.push(...) — to'g'ridan-to'g'ri o'zgartirish |
assign({ items: ({ context }) => [...] }) |
| taymer to'langan buyurtmaga eslatma yubordi | setTimeout qo'lda, holatdan chiqqanda tozalanmagan |
after: { ms: "holat" } |
| eski so'rov natijasi yangi holatni buzdi | async ish holatga bog'lanmagan | invoke + fromPromise (holatdan chiqilsa — e'tiborsiz) |
| holatlar soni portladi (9, 12, …) | mustaqil jarayonlar bitta jadvalda | type: "parallel" |
| guard nomi bo'yicha xato | setup({ guards }) da e'lon qilinmagan |
nomli guard/amallar — setup ichida |
| oddiy savatga mashina — ko'p kod | holat yo'q, faqat ma'lumot | @xstate/store, Zustand yoki useReducer |
7. Mashqlar
Mashqlar kurs/mashqlar/19/28-xstate/ da. 2-mashq — XState bilan Node'da (o'z papkangizda xstate@5.33.2, .mjs fayl); 3-mashq — saytda.
1-mashq (oson): jadvalni o'qing
«Jonli model» bo'limidagi machine jadvali bo'yicha javob bering:
savatdaholatidaOKkeldi → holat: (rad etildi)tolanmoqdaholatidaFAILkeldi → holat:xatoholatidaPAYkeldi → holat:tolandiholatida qabul qilinadigan hodisalar soni:
Yechim
OK faqat tolanmoqda da ma'noli — savatda da rad etiladi va holat o'sha qoladi. Keyin FAIL → xato, undan PAY → yana tolanmoqda (qayta urinish). tolandi: {} — bo'sh: yakuniy holat, hech bir hodisa qabul qilinmaydi (XState'da — type: "final").
2-mashq (o'rta): stol broni guard bilan
XState bilan stol mashinasi: bosh → BOOK (guard: mehmonlar ≤ 6, kontekstga guests yoziladi) → band → FREE → bosh (guests 0). Ketma-ketlik: 8 kishilik bron (rad), 4 kishilik (qabul), 2 kishilik (band stolga — rad), bo'shatish. Har hodisadan keyin holat va mehmonlar sonini chiqaring.
Ishora: hodisaga qarab guard — ({ event }) => event.guests <= 6.
Yechim
import { assign, createActor, setup } from "xstate";
const tableMachine = setup({
guards: {
fits: ({ event }) => event.guests <= 6,
},
}).createMachine({
id: "stol",
initial: "bosh",
context: { guests: 0 },
states: {
bosh: {
on: {
BOOK: {
guard: "fits",
target: "band",
actions: assign({ guests: ({ event }) => event.guests }),
},
},
},
band: {
on: {
FREE: {
target: "bosh",
actions: assign({ guests: 0 }),
},
},
},
},
});
const actor = createActor(tableMachine).start();
const show = () => {
const { value, context } = actor.getSnapshot();
console.log(value, context.guests);
};
actor.send({ type: "BOOK", guests: 8 });
show();
actor.send({ type: "BOOK", guests: 4 });
show();
actor.send({ type: "BOOK", guests: 2 });
show();
actor.send({ type: "FREE" });
show();Node 24, xstate 5.33.2:
bosh 0
band 4
band 4
bosh 0Ikki xil rad etish: 8 kishi — bosh da BOOK bor, lekin guard false; 2 kishi — band da BOOK umuman yo'q. Ikkalasida ham holat va kontekst o'zgarmadi — "band stolga ustma-ust bron" kabi xato tug'ilmadi.
3-mashq (qiyin): kutubxonasiz after
O'quv modeliga kechiktirilgan o'tish qo'shing. Mashina: kutilmoqda → (PAID) → tolandi; kutilmoqda da 200 ms qolinsa → eslatma; eslatma → (PAID) → tolandi. Talab: taymer faqat kutilmoqda da ishlasin va holatdan chiqilganda tozalansin.
Ishora: useEffect bog'liqligi — joriy holat; effekt kutilmoqda da setTimeout qo'yadi va cleanup da clearTimeout.
Yechim
import { useEffect, useReducer } from "react";
const machine = {
kutilmoqda: { PAID: "tolandi", TIMEOUT: "eslatma" },
eslatma: { PAID: "tolandi" },
tolandi: {},
};
const after = { kutilmoqda: { ms: 200, event: "TIMEOUT" } };
function transition(state, event) {
const next = machine[state][event.type] ?? state;
console.log(`${state} + ${event.type} → ${next}`);
return next;
}
export default function App() {
const [state, send] = useReducer(transition, "kutilmoqda");
useEffect(() => {
const timer = after[state];
if (timer === undefined) return;
const id = setTimeout(
() => send({ type: timer.event }),
timer.ms,
);
// holatdan chiqildi — taymer yo'q
return () => clearTimeout(id);
}, [state]);
return (
<>
<p>To'lov: {state}</p>
<button onClick={() => send({ type: "PAID" })}>To'landi</button>
</>
);
}Konsolda:
kutilmoqda + TIMEOUT → eslatma
eslatma + PAID → tolandiEkranda:
To'lov: tolandi
To'landiAgar «To'landi» 200 ms dan oldin bosilsa: kutilmoqda + PAID → tolandi, keyin effekt tozalash funksiyasi taymerni o'chiradi — TIMEOUT hech qachon kelmaydi. Shu "tozalashni unutmaslik" XState'da after ichiga qurilgan.
4-mashq (tahlil): mashina kerakmi?
Har vazifa uchun ayting: XState, @xstate/store, useReducer + status yoki oddiy useState?
- Tugma bosilganda ochilib-yopiladigan yon panel.
- Savat: qo'shish, o'chirish, jami summa.
- Kuryer ilovasi: buyurtma qabul → oshxonada → yo'lda → yetkazildi; har bosqichda bekor qilish; 10 daqiqa javobsiz — boshqa kuryerga; GPS va chat bir vaqtda ishlaydi.
Yechim
useState(open: boolean) — ikki holat, bitta hodisa.@xstate/store, Zustand yokiuseReducer— holatlar yo'q, ma'lumot va amallar bor. XState mashinasi bu yerda tajribadagi "qimmat reducer" bo'lardi.- XState — ketma-ket bosqichlar, ota holatdagi "bekor qilish" (ichma-ich), taymer (
after), parallel hududlar (GPS, chat), server so'rovlari (invoke). Aynan statechart uchun vazifa.
8. Real ishda
- To'lov va onboarding oqimlari — XState'ning eng ko'p uchraydigan joyi (bank, fintech, telekom). Diagramma talablar hujjatining bir qismi bo'ladi.
- UI kutubxonalari ichida — Zag (Ark UI, Chakra UI 3): siz mashina yozmasangiz ham, uni ishlatasiz.
- Test: mashina sof funksiyaga yaqin — har holat va o'tishni React'siz, Node'da tekshirish oson (bugungi misollar kabi).
@xstate/graphbarcha yo'llarni avtomatik sanab, test ssenariylarini yasaydi. - Backend'da ham: XState aktorlari Node'da ishlaydi (navbat ishlovchilari, uzoq jarayonlar).
- Intervyu savollari: "holat mashinasi nima va qanday muammoni hal qiladi?", "imkonsiz holat nima?", "statechart oddiy mashinadan nimasi bilan farq qiladi?", "guard va action farqi?", "qachon XState ortiqcha?".
Xulosa
- Holat mashinasi: aniq holatlar, boshlang'ich holat va o'tishlar jadvali; jadvalda yo'q hodisa — rad etiladi, imkonsiz holat tug'ilmaydi.
- XState 5:
setup(nomli guard, amal, aktorlar) +createMachine;contextvaassign— ma'lumot; guard — shartli o'tish. after— vaqt hodisasi (holatdan chiqilsa bekor),type: "parallel"— mustaqil hududlar, ichma-ich holat — umumiy qoidalar ota holatda;invoke+fromPromise— holatga bog'langan async ish.- React'da
useMachine→[snapshot, send];snapshot.can()vamatches()— tugmalar va ko'rinish mashinadan. @xstate/store— mashinasiz kichik store; Zag — mashinali UI vidjetlari; Stately — vizualizator.- Tajriba: oddiy ro'yxatda XState +12.9 KB va 42 qator — ortiqcha;
vazifalarwizard'i RHF qadamlari bilan yetdi. XState — tarmoqlanuvchi, vaqtli, parallel oqimlar uchun.
Keyingi dars: React Hook Form I: useForm, register va xatolar — formalarni uncontrolled inputlar va kam render bilan qurish; vazifalar da tahrir formasi.
Manbalar
- XState v5: Machines,
setup, Guards, Delayed transitions, Parallel states, Invoke — stately.ai/docs (2026-10 holatiga) @xstate/react:useMachine,useSelector;@xstate/store— stately.ai/docs- Zag — zagjs.com; Stately visualizer — stately.ai/viz
- D. Harel, "Statecharts: A Visual Formalism for Complex Systems" (1987)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!