Mundarija (28)
- Bu darsda
- 1. Nega bu kerak?
- 2. Oddiy o'zgaruvchi nega ishlamaydi
- 3. useState
- 3.1 Birinchi state
- 3.2 Setter chaqirilganda nima bo'ladi
- 4. Bir nechta state
- 5. Har nusxaning o'z state'i
- 6. Boshlang'ich qiymat
- 6.1 Faqat birinchi chizishda
- 6.2 Lazy init: og'ir hisob bir marta
- 7. TypeScript va state
- 8. Ikki qoida va bitta xato
- 8.1 Hooklar qoidasi
- 8.2 Render paytida setter: cheksiz sikl
- 9. Vazifalar qadami: forma xotirasi — useState
- 9.1 Reja
- 9.2 YangiVazifaForma.tsx
- 9.3 Komponent testlari
- 10. Ko'p uchraydigan xatolar
- 11. Mashqlar
- 1-mashq (oson): render jadvali
- 2-mashq (o'rta): stol bron qilish
- 3-mashq (qiyin): xato xabarli forma (TypeScript)
- 4-mashq: Vazifalar qadami
- 12. Real ishda
- Xulosa
- Manbalar
State I: useState — React komponentining xotirasi va qayta render
Qisqacha: State — komponentning chizishlar orasida saqlanadigan xotirasi.
const [count, setCount] = useState(0)—countjoriy qiymat,setCountuni o'zgartiradi va React'ga "qayta chiz" deydi. React komponent funksiyasini qayta chaqiradi,useStateesa endi yangi qiymatni qaytaradi. Oddiyletbunday ishlamaydi: u har chaqiruvda noldan boshlanadi va o'zgarsa, React buni bilmaydi. Har komponent nusxasining state'i o'ziniki. Boshlang'ich qiymat faqat birinchi chizishda ishlatiladi.
Bu darsda
- Nega oddiy o'zgaruvchi ekranni yangilamasligini ikki sabab bilan tushuntira olasiz.
useStatebilan state e'lon qilasiz, uni o'qiysiz va setter bilan o'zgartirasiz.- Setter chaqirilgandan keyin nima bo'lishini qadamma-qadam (render jadvali bilan) ayta olasiz.
- Bir komponentda bir nechta state ishlatasiz va har nusxaning o'z state'i borligini ko'rasiz.
- State'ga TypeScript turi berasiz va boshlang'ich qiymat tuzoqlarini taniysiz.
Oldin bilishingiz kerak: Hodisalarni boshqarish, Massiv destructuring, Holatdan UI chizish: state → render.
1. Nega bu kerak?
Oldingi darslarda ekran o'zgarishi uchun draw() funksiyasini o'zimiz chaqirdik: vanilla o'zgaruvchi yangilandi → root.render(...) qaytadan. Bu JSX II dan beri ishlatgan "ko'prik". U ishlaydi, lekin u v6 dagi render() ning o'zi. Holat komponentdan tashqarida, uni yangilash va chizishni eslab qolish — sizning vazifangiz.
React'ning va'dasi boshqa edi: UI = f(state). Holat o'zgaradi — ekran o'zi ergashadi. Buning uchun komponent o'z xotirasiga ega bo'lishi kerak. Bu xotira React'ga "meni kuzat" deb aytishi ham kerak. Bunday xotira — state, uni beradigan funksiya — useState.
Hayotiy o'xshatish: ofitsiantning bloknoti. Mijoz "yana bitta choy" desa, ofitsiant bloknotdagi raqamni o'zgartiradi va oshxonaga xabar beradi. Raqamni boshida saqlamasa — har safar noldan boshlaydi. Xabar bermasa — oshxona bilmaydi. useState ikkalasini ham qiladi: eslab qoladi va xabar beradi.
2. Oddiy o'zgaruvchi nega ishlamaydi
React nima va nega darsida bu xatoni bir marta ko'rgandik. Endi uni ichidan ko'ramiz — console.log bilan:
function App() {
let cups = 0; // ❌ oddiy o'zgaruvchi
console.log("App chaqirildi, cups =", cups);
function handleClick() {
cups = cups + 1;
console.log("bosildi, cups =", cups);
}
return (
<>
<p>Ko'k choy: {cups} ta</p>
<button onClick={handleClick}>Choy +</button>
</>
);
}Konsolda:
App chaqirildi, cups = 0
bosildi, cups = 1
bosildi, cups = 2Ekranda:
Ko'k choy: 0 ta
Choy +cups haqiqatan 2 bo'ldi — konsol buni ko'rsatyapti. Ekranda esa 0. Ikki sabab bor:
- React bilmaydi. O'zgaruvchiga yangi qiymat yozish React'ga hech narsa demaydi. "App chaqirildi" faqat bir marta chiqdi — React komponentni qayta chaqirmadi.
- Qayta chaqirsa ham, yo'qoladi.
let cups = 0— funksiyaning lokal o'zgaruvchisi. Funksiya har chaqiruvda uni yana0bilan boshlaydi (Scope asoslari). Xotira chaqiruvlar orasida yashamaydi.
Bizga ikkala muammoni yechadigan narsa kerak: chaqiruvlar orasida saqlanadigan qiymat va uni o'zgartirganda React'ga xabar.
3. useState
3.1 Birinchi state
import { useState } from "react";
function App() {
const [cups, setCups] = useState(0);
console.log("App chaqirildi, cups =", cups);
function handleClick() {
setCups(cups + 1);
}
return (
<>
<p>Ko'k choy: {cups} ta</p>
<button onClick={handleClick}>Choy +</button>
</>
);
}Konsolda:
App chaqirildi, cups = 0
App chaqirildi, cups = 1
App chaqirildi, cups = 2Ekranda:
Ko'k choy: 2 ta
Choy +Endi "App chaqirildi" uch marta chiqdi: birinchi chizish va ikki bosish. Har safar cups yangi qiymat bilan keldi. Qatorma-qator:
import { useState } from "react"—useStateReact'ning funksiyasi.usebilan boshlanadigan bunday funksiyalar hook deyiladi: ular komponentni React'ning ichki imkoniyatlariga "ilib" ulaydi.useState(0)— "menga xotira katagi ber, boshlanishi — 0". U ikki elementli massiv qaytaradi.const [cups, setCups] = …— massiv destructuring: birinchisi — joriy qiymat, ikkinchisi — uni o'zgartiruvchi funksiya (setter). Nomlash odati:[narsa, setNarsa].setCups(cups + 1)— yangi qiymatni beradi va React'ga "qayta chiz" deydi.
3.2 Setter chaqirilganda nima bo'ladi
Bosishdan ekrangacha bo'lgan yo'lni qadamma-qadam yozamiz:
| Qadam | State | Ekranda |
|---|---|---|
birinchi chizish: useState(0) → [0, setCups] |
cups: 0 |
Ko'k choy: 0 ta |
bosildi: setCups(0 + 1) — React navbatga yozadi |
cups: 0 (hali) |
Ko'k choy: 0 ta |
React App ni qayta chaqiradi: useState(0) → [1, setCups] |
cups: 1 |
Ko'k choy: 1 ta |
bosildi: setCups(1 + 1), qayta chaqiruv |
cups: 2 |
Ko'k choy: 2 ta |
Ikkinchi qatorga e'tibor bering: setter cups o'zgaruvchisini o'zgartirmaydi. U React'ga "keyingi chizishda 1 bo'lsin" deydi. Qiymat React ichida saqlanadi, komponentdan tashqarida. Qayta chaqirilgan App da useState(0) qatori yana bajariladi, lekin bu safar React boshlang'ich 0 ga qaramaydi. U saqlab qo'ygan 1 ni qaytaradi. Bu nozik joyni keyingi darsda chuqur ko'ramiz.
sequenceDiagram
participant U as Foydalanuvchi
participant K as App komponenti
participant R as React
U->>K: «Choy +» bosildi
K->>R: setCups(1)
R->>R: qiymatni saqlaydi, chizishni rejalashtiradi
R->>K: App() ni qayta chaqiradi
K->>R: useState → 1, yangi JSX
R->>U: DOM'da faqat "0" → "1"Diagrammada uch ishtirokchi: foydalanuvchi, komponent va React. Komponent React'ga faqat so'rov yuboradi. Qayta chaqirish va DOM'ni yangilash — React'ning ishi. DOM'da faqat raqam o'zgaradi: <p> va tugma o'sha-o'sha qoladi (React nima va nega dagi "faqat farqini").
Tekshirib ko'ring:
setCups(cups + 1)dan keyingi qatordaconsole.log(cups)yozsak, nima chiqadi — eski qiymatmi, yangimi?
Javob
Eski. cups — shu chaqiruvning const o'zgaruvchisi, setter uni o'zgartira olmaydi. Yangi qiymat faqat keyingi chizishda, App qayta chaqirilganda keladi. Bu — keyingi darsning asosiy g'oyasi: snapshot.
4. Bir nechta state
Komponentda istalgancha useState bo'lishi mumkin. Har biri — alohida katak:
import { useState } from "react";
function App() {
const [name, setName] = useState("");
const [guests, setGuests] = useState(1);
const total = guests * 35000; // state emas — hisoblanadi
return (
<>
<input
aria-label="Ism"
value={name}
onChange={(event) => setName(event.target.value)}
/>
<button onClick={() => setGuests(guests + 1)}>Mehmon +</button>
<p>
Bron: {name === "" ? "ism yo'q" : name}, {guests} kishi
</p>
<p>Osh uchun: {total} so'm</p>
</>
);
}Ekranda:
Mehmon +
Bron: Malika, 3 kishi
Osh uchun: 105000 so'mUch narsa:
name— satr,guests— son. State istalgan qiymat bo'la oladi: satr, son, boolean, massiv, obyekt. Obyekt va massiv bilan ishlashning o'z qoidalari bor — State III darsida.total— state emas. Uguestsdan har chizishda hisoblanadi. React nima va nega dagi qoida: hisoblab bo'ladigan narsani state'da saqlamang. Aks holda ikkalasini birga yangilashni unutish mumkin (State strukturasini loyihalash).value={name}vaonChange— maydon matni state'dan keladi va har harfda state'ga qaytadi. Bunday maydon controlled (boshqariladigan) deyiladi. Hodisalar darsidagi katak ham shunday edi. Formalardagi tafsilotlar — Formalar I darsida.
5. Har nusxaning o'z state'i
Komponentni bir necha marta ishlatsangiz, har nusxa o'z xotirasini oladi:
import { useState } from "react";
function DishCounter({ name }) {
const [count, setCount] = useState(0);
return (
<li>
{name}: {count}{" "}
<button
aria-label={`${name} +`}
onClick={() => setCount(count + 1)}
>
+
</button>
</li>
);
}
function App() {
return (
<ul>
<DishCounter name="Osh" />
<DishCounter name="Manti" />
<DishCounter name="Choy" />
</ul>
);
}Ekranda:
Osh: 2 +
Manti: 0 +
Choy: 1 +Funksiya bitta, xotira uchta. React state'ni funksiyaga emas, daraxtdagi o'ringa bog'laydi: "<ul> ning birinchi bolasi — DishCounter, uning state'i — 2". Shuning uchun Osh, Manti va Choy bir-biriga xalal bermaydi. O'rin o'zgarsa yoki key o'zgarsa nima bo'lishi — State'ning saqlanishi va key bilan reset darsida.
State shaxsiy: App uchta count ning birortasini ham o'qiy olmaydi. "Jami nechta taom" kerak bo'lsa — state'ni otaga ko'tarish kerak (State'ni ko'tarish).
6. Boshlang'ich qiymat
6.1 Faqat birinchi chizishda
useState(start) dagi qiymat faqat birinchi chizishda ishlatiladi. Keyin React o'zining saqlaganini qaytaradi. Bu props'dan state yasaganda tuzoqqa olib keladi:
import { useState } from "react";
import { createRoot } from "react-dom/client";
const root = createRoot(document.getElementById("root"));
let start = 1;
function Counter({ start }) {
const [count, setCount] = useState(start); // faqat birinchi marta
return (
<p>
start={start}, count={count}
</p>
);
}
function draw() {
root.render(
<>
<Counter start={start} />
<button onClick={() => { start = start + 10; draw(); }}>
Keyingi
</button>
</>
);
}
draw();Ekranda:
start=21, count=1
KeyingiOta start ni ikki marta o'zgartirdi — props 21. State esa birinchi qiymatda — 1 — qotib qoldi. Bu xato emas, ataylab shunday: state — komponentning o'z xotirasi, props esa faqat "boshlanishi". Props bilan doim mos bo'lishi kerak bo'lsa, state umuman kerak emas — props'ning o'zini ishlating. Bu holatlar — State strukturasini loyihalash darsida.
6.2 Lazy init: og'ir hisob bir marta
Boshlang'ich qiymatni hisoblash qimmat bo'lsa-chi? Masalan, localStorage dan o'qish yoki katta massivni qayta ishlash:
import { useState } from "react";
function readSaved(label) {
console.log(label, "— o'qildi");
return 0;
}
function App() {
// A: readSaved har chizishda chaqiriladi
const [a, setA] = useState(readSaved("A"));
// B: funksiyaning o'zi — React faqat birinchi chizishda chaqiradi
const [b, setB] = useState(() => readSaved("B"));
return (
<button onClick={() => { setA(a + 1); setB(b + 1); }}>+</button>
);
}Konsolda:
A — o'qildi
B — o'qildi
A — o'qildi
A — o'qildiEkranda:
+useState(readSaved("A")) — JavaScript argumentni avval hisoblaydi. Shuning uchun readSaved har chizishda chaqiriladi, natijasi esa birinchidan keyin tashlab yuboriladi. useState(() => readSaved("B")) da esa funksiyaning o'zi beriladi (Hodisalar dagi f va f() farqi). React uni faqat birinchi chizishda chaqiradi. Bu lazy init (dangasa boshlash). vazifalar da tema tanlovi aynan shunday localStorage dan o'qiladi (Mini-ilova darsida).
7. TypeScript va state
TypeScript turni boshlang'ich qiymatdan chiqaradi: useState(0) — number, useState("") — string. Tuzoq — null bilan boshlash:
import { useState } from "react";
export function App() {
const [name, setName] = useState("");
const [error, setError] = useState(null);
function check() {
setName(5);
setError("Ism kiritilmagan");
}
return (
<p onClick={check}>
{name} {error}
</p>
);
}Xato:
TS2345: Argument of type 'number' is not assignable to parameter of type 'SetStateAction<string>'.
TS2345: Argument of type '"Ism kiritilmagan"' is not assignable to parameter of type 'SetStateAction<null>'.Birinchisi — kutilgan himoya: name satr, son yozib bo'lmaydi. Ikkinchisi — tuzoq. useState(null) dan TypeScript turni null deb oldi: "bu katakda faqat null bo'ladi". Xato matni hech qachon kiritib bo'lmaydi. Yechim — turni aniq yozish, generic bilan (Generics):
import { useState } from "react";
export default function App() {
const [error, setError] = useState<string | null>(null);
return (
<>
<button onClick={() => setError("Ism kiritilmagan")}>
Tekshirish
</button>
<p role="status">{error}</p>
</>
);
}Ekranda:
TekshirishuseState<string | null>(null) — "bu katakda satr yoki null, boshlanishi null". vazifalar dagi forma xatosi aynan shunday e'lon qilinadi. SetStateAction<string> — setter qabul qiladigan narsa: yangi qiymat yoki funksiya (keyingi dars dagi updater).
8. Ikki qoida va bitta xato
8.1 Hooklar qoidasi
useState ni faqat komponentning yuqori darajasida chaqiring — if, sikl yoki ichki funksiya ichida emas. React state kataklarini chaqiruv tartibi bo'yicha taniydi: "birinchi useState — name, ikkinchisi — guests". Shart ichida bo'lsa, tartib chizishdan chizishga o'zgaradi va kataklar almashib ketadi. Linter buni ushlaydi (vazifalar dagi react-hooks/rules-of-hooks). Mana haqiqiy chiqish: useState erta return dan keyin yozilgan fayl:
7:27 error React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return? react-hooks/rules-of-hooks"useState shartli chaqirilyapti. Hooklar har chizishda aynan bir xil tartibda chaqirilishi kerak. Erta return dan keyin chaqirib qo'ymadingizmi?" Nega aynan shunday — Hooklar qoidalari darsida. TypeScript bu xatoni ko'rmaydi — faqat linter.
8.2 Render paytida setter: cheksiz sikl
Hodisalar darsida va'da qilingan xato:
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
return <button onClick={setCount(count + 1)}>{count}</button>;
}Konsolda:
Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop."Juda ko'p qayta chizish. React cheksiz sikl bo'lmasligi uchun chizishlar sonini cheklaydi." setCount(count + 1) chizish paytida chaqirildi → React qayta chizishni rejalashtirdi → qayta chizishda yana setCount → … React bir necha o'n marta urinib, to'xtatdi. Saytdagi xato qutisida Maslahat ham bor: onClick={() => setSon(son + 1)} deb yozing. Tuzatish — arrow funksiya.
9. Vazifalar qadami: forma xotirasi — useState
9.1 Reja
Ro'yxat React'da (Hodisalar). Endi navbat — "Yangi vazifa" formasi. Uch dars davomida bitta branch:
git switch -c refactor/forma-usestateFormada nimani eslab qolish kerak? v6 da "xato bormi" degan savolga DOM javob berardi: maydon.getAttribute("aria-invalid") !== "true". Teg takliflari ham DOM'da yashardi: tegTakliflari.hidden, replaceChildren(...tugmalar). Endi ikkalasi state bo'ladi, atributlar esa state'dan chiziladi.
9.2 YangiVazifaForma.tsx
src/YangiVazifaForma.tsx — asosiy qismlari (to'liq fayl 162 qator). Uzun izoh qatorlari telefon uchun qayta bo'lingan:
// YangiVazifaForma.tsx — yangi vazifa: maydon, xato va teg
// takliflari (17/#12: useState). Forma o'zi eslab qoladigan ikki
// narsa — xato matni va takliflar — komponentning state'i:
// o'zgarsa, React formani qayta chizadi. v6 da "xato bormi"
// savoliga DOM javob berardi (aria-invalid atributi), endi — state,
// atribut esa undan chiziladi
import { useState } from "react";
…
export function YangiVazifaForma({
boshlangichMatn,
xatoniTop,
takliflarniTop,
onYoz,
onQosh,
}: Props) {
// useState(boshlang'ich) → [joriy qiymat, uni almashtiruvchi
// funksiya]
const [xato, setXato] = useState<string | null>(null);
const [takliflar, setTakliflar] = useState<readonly string[]>([]);Ikki state, ikkalasi ham «TypeScript va state» bo'limidagi qoida bilan. xato — string | null, takliflar — faqat o'qiladigan satrlar massivi. Ular qayerda o'zgaradi:
function yuborildi(event: FormEvent<HTMLFormElement>) {
event.preventDefault(); // sahifa qayta yuklanmasin
const maydon = maydonniOl(event.currentTarget);
if (maydon === null) {
return;
}
const natija = onQosh(maydon.value.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() maydonga qoralamani
// qaytarardi (o'lchandi)
maydon.value = "";
return;
default: {
const qolmadi: never = natija;
void qolmadi;
}
}
}onQosh — ota bergan funksiya. U QoshishNatijasi qaytaradi — Shartli render darsidagi discriminated union naqshi, never bilan. src/turlar.ts ga qo'shiladi:
export type QoshishNatijasi =
| { readonly tur: "qoshildi" } // maydon tozalanadi
// internet yo'q — matn maydonda qoladi:
| { readonly tur: "saqlanmadi" }
| { readonly tur: "xato"; readonly xabar: string };Forma natijaga qarab o'z state'ini yangilaydi. setXato(natija.xabar) — xato matni paydo bo'ladi. Endi u chizishda qanday ishlatiladi:
<input
id="yangi-matn"
name="matn"
required
maxLength={Vazifa.MAKS_UZUNLIK}
autoComplete="off"
defaultValue={boshlangichMatn}
aria-invalid={xato !== null ? true : undefined}
aria-describedby={xato !== null ? XATO_ID : undefined}
onInput={(event) => ozgardi(event.currentTarget)}
onKeyDown={maydondaTugma}
/>
…
<p id={XATO_ID} className="xato" role="status">
{xato}
</p>v6 dagi xatoniKorsat() va xatoniTozala() — har biri uch qator DOM buyrug'i — yo'qoldi. Ularning o'rnida ikki atribut va bitta {xato}, uchalasi ham state'dan. aria-invalid ga false emas, undefined — Shartli render dagi qoida. Malika'ning ekran o'quvchisi role="status" paragrafidagi xatoni o'qib beradi.
Teg takliflari ham state'dan chiziladi:
<div
id="teg-takliflari"
role="group"
aria-label="Teg takliflari"
hidden={takliflar.length === 0}
onKeyDown={takliflardaTugma}
>
{takliflar.map((teg) => (
<button
key={teg}
type="button"
data-teg={teg}
onClick={(event) => tegniQoy(teg, event.currentTarget.form)}
>
{teg}
</button>
))}
</div>v6 dagi replaceChildren(...takliflar.map(…createElement("button")…)) — endi map va key={teg} (Ro'yxatlar va key): teg nomlari takrorlanmaydi.
Maydonning o'zi esa uncontrolled — defaultValue, value emas. Matn DOM'da turadi, forma uni FormData siz, maydon.value bilan o'qiydi. Nega bu yerda controlled emas, va form.reset() nega ishlatilmagani — izohda "o'lchandi" deyilgan. Kanonda sinaldi: React defaultValue ni value atributiga ham yozadi, reset() esa maydonni atributdagi qiymatga — qoralamaga — qaytaradi. Tafsilotlari Formalar II darsida.
9.3 Komponent testlari
Forma hali sahifaga ulanmagan — uni asosiy.ts va yangi holat bilan birga State III darsida ulaymiz. Lekin komponentni hozirdanoq sinash mumkin: test unga props'ni o'zi beradi. Yangi fayl tekshiruv/YangiVazifaForma.test.tsx da oltita test bor. Ulardan biri — state'ning asosiy ishi:
test("xato: matni ko'rinadi, maydon aria-invalid va aria-describedby", () => {
const { maydon, yoz } = chiz({
tur: "xato",
xabar: "Vazifa matnini yozing.",
});
yoz("");
fireEvent.submit(maydon.form!);
expect(screen.getByRole("status").textContent).toBe(
"Vazifa matnini yozing.",
);
expect(maydon.getAttribute("aria-invalid")).toBe("true");
…
// Tuzatildi — xato o'sha zahoti yo'qoladi (state)
yoz("Non olish");
expect(screen.getByRole("status").textContent).toBe("");
expect(maydon.hasAttribute("aria-invalid")).toBe(false);
});npx vitest run --reporter=verbose (yangi fayl qatorlari):
✓ tekshiruv/YangiVazifaForma.test.tsx > YangiVazifaForma > boshlang'ich qiymat — qoralama 147ms
✓ tekshiruv/YangiVazifaForma.test.tsx > YangiVazifaForma > xato: matni ko'rinadi, maydon aria-invalid va aria-describedby 41ms
✓ tekshiruv/YangiVazifaForma.test.tsx > YangiVazifaForma > qo'shildi — maydon bo'shaydi (qoralamaga qaytmaydi) 9ms
✓ tekshiruv/YangiVazifaForma.test.tsx > YangiVazifaForma > saqlanmadi (oflayn) — matn maydonda qoladi 7ms
✓ tekshiruv/YangiVazifaForma.test.tsx > YangiVazifaForma > teg takliflari: # dan keyin chiqadi, tanlansa — qo'yiladi va yopiladi 25ms
✓ tekshiruv/YangiVazifaForma.test.tsx > YangiVazifaForma > Esc — takliflar yopiladi, matn joyida 13ms
Test Files 2 passed (2)
Tests 14 passed (14)8 ta eski va 6 ta yangi. npm run tip va npm run lint — toza. Commit hali yo'q — branch State III darsida tugaydi.
10. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| bosildi, ekran o'zgarmadi | oddiy let |
useState |
Too many re-renders |
renderda setter: onClick={setX(…)} |
onClick={() => setX(…)} |
| props o'zgardi, state eski | useState(props.x) faqat birinchi marta |
props'ni to'g'ridan-to'g'ri ishlating |
| sahifa sekin, har chizishda og'ir hisob | useState(hisobla()) |
useState(() => hisobla()) |
SetStateAction<null> xatosi |
useState(null) |
useState<string | null>(null) |
useState is called conditionally |
hook if ichida yoki return dan keyin |
yuqori darajaga |
| setter'dan keyin eski qiymat | snapshot | State II |
11. Mashqlar
Mashqlar kurs/mashqlar/17/12-usestate/ da. Har yechimni «Ishga tushir» bilan sinang.
1-mashq (oson): render jadvali
function Tea() {
const [cups, setCups] = useState(2);
return (
<>
<p>Choy: {cups}</p>
<button onClick={() => setCups(cups * 2)}>×2</button>
<button onClick={() => setCups(0)}>Tozalash</button>
</>
);
}Mijoz "×2", "×2", "Tozalash", "×2" ni bosdi. Jadvalni to'ldiring:
| Qadam | State | Ekranda |
|---|---|---|
| boshida | cups: 2 |
Choy: 2 |
| «×2» | cups: 4 |
Choy: 4 |
| «×2» | cups: |
Choy: 8 |
| «Tozalash» | cups: 0 |
Choy: 0 |
| «×2» | cups: |
Choy: 0 |
Yechim
8 va 0. Oxirgi qator — 0 * 2 = 0. Setter har safar joriy state'dan hisoblaydi, boshlang'ich 2 ga qaytmaydi: useState(2) dagi 2 faqat birinchi chizishda ishlatildi.
2-mashq (o'rta): stol bron qilish
Booking komponentini yozing. State: guests (1 dan boshlanadi), tea (boolean, false). Talablar:
- "−" va "+" tugmalari mehmonlar sonini o'zgartiradi, "−" 1 da
disabled, "+" 8 dadisabled; - "Ko'k choy" katagi (
<input type="checkbox">, controlled:checked={tea}vaonChange); - pastda: "4 kishi, osh: 140000 so'm" va choy tanlansa — "+ choy: 20000 so'm" (har kishiga 5000);
- jami summa state'da emas — hisoblanadi.
Blok: "+" uch marta, katakni bosadi. Ekranda:
−+
Ko'k choy
4 kishi, osh: 140000 so'm
+ choy: 20000 so'm
Jami: 160000 so'mYechim
import { useState } from "react";
function Booking() {
const [guests, setGuests] = useState(1);
const [tea, setTea] = useState(false);
const plov = guests * 35000;
const teaPrice = tea ? guests * 5000 : 0;
return (
<>
<div>
<button
disabled={guests === 1}
onClick={() => setGuests(guests - 1)}
>
−
</button>
<button
disabled={guests === 8}
onClick={() => setGuests(guests + 1)}
>
+
</button>
</div>
<label>
<input
type="checkbox"
checked={tea}
onChange={(event) => setTea(event.target.checked)}
/>
Ko'k choy
</label>
<p>
{guests} kishi, osh: {plov} so'm
</p>
{tea && <p>+ choy: {teaPrice} so'm</p>}
<p>Jami: {plov + teaPrice} so'm</p>
</>
);
}
function App() {
return <Booking />;
}Ekranda:
−+
Ko'k choy
4 kishi, osh: 140000 so'm
+ choy: 20000 so'm
Jami: 160000 so'm{tea && …} — tea boolean, 0 tuzog'i yo'q (Shartli render). plov, teaPrice va jami — state emas: ular guests va tea dan hisoblanadi va hech qachon "eskirib" qolmaydi.
3-mashq (qiyin): xato xabarli forma (TypeScript)
«Bahor» telefon raqami formasi, .tsx da. Uncontrolled <input name="phone"> va "Saqlash" tugmasi. State: error: string | null va saved: string | null.
- Yuborishda (
onSubmit,preventDefault) raqamFormDatadan o'qiladi va probellar olib tashlanadi; +998bilan boshlanib, jami 13 belgi bo'lmasa —setError("Raqam +998 bilan va 12 raqamdan iborat bo'lsin"), maydondaaria-invalid;- to'g'ri bo'lsa —
setError(null),setSaved(raqam), ekranda "Saqlandi: +998900000000".
Blok noto'g'ri raqam yozib yuboradi — to'g'risini o'zingiz sinaysiz. Ishora: onInput kerak emas — tekshiruv faqat yuborishda.
Yechim
import { useState } from "react";
import type { FormEvent } from "react";
export default function App() {
const [error, setError] = useState<string | null>(null);
const [saved, setSaved] = useState<string | null>(null);
function handleSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const raw = new FormData(event.currentTarget).get("phone");
const phone = String(raw ?? "").replaceAll(" ", "");
if (!phone.startsWith("+998") || phone.length !== 13) {
setError("Raqam +998 bilan va 12 raqamdan iborat bo'lsin");
return;
}
setError(null);
setSaved(phone);
}
return (
<form onSubmit={handleSubmit}>
<input
name="phone"
aria-label="Telefon"
aria-invalid={error !== null ? true : undefined}
/>
<button type="submit">Saqlash</button>
<p role="status">{error}</p>
{saved !== null && <p>Saqlandi: {saved}</p>}
</form>
);
}Ekranda:
Saqlash
Raqam +998 bilan va 12 raqamdan iborat bo'lsinBlok noto'g'ri raqamni yubordi — xato paragrafi to'ldi. Natija oynasida endi to'g'ri raqam yozing: +998 90 000 00 00. Yuborilganda setError(null) xatoni bo'shatadi va "Saqlandi: +998900000000" paydo bo'ladi. String(raw ?? "") — FormData.get string | File | null qaytaradi (FormData). TypeScript satr metodlarini faqat satrda ruxsat etadi.
4-mashq: Vazifalar qadami
refactor/forma-usestate branch'ida src/YangiVazifaForma.tsx, turlar.ts dagi QoshishNatijasi va tekshiruv/YangiVazifaForma.test.tsx ni kanondagidek yozing. npm test — Vitest 14/14, npm run tip toza bo'lsin. Savol: forma hali sahifaga ulanmagan — npm run knip buni topadimi?
Yechim
knip jim tugaydi — biz ham shuni kutmagandik. Sababi: knip Vitest sozlamasini o'qiydi va test fayllarini ham kirish nuqtasi deb hisoblaydi. YangiVazifaForma.test.tsx komponentni import qiladi — demak u "ishlatilgan". Saboq: knip toza bo'lishi — komponent sahifada ekanini anglatmaydi. Forma hali ilovada yo'q, buni faqat brauzerda ko'rasiz. State III darsida render.tsx dagi formaniChiz() uni sahifaga ulaydi.
12. Real ishda
useState— React'dagi eng ko'p ishlatiladigan hook: ochiq-yopiq menyu, tanlangan tab, forma maydonlari, yuklanish holati.- Kod ko'rigida ko'p uchraydigan izohlar: "bu state emas, hisoblang", "
useState(props.x)— sinxron bo'lmaydi", "useState(null)ga tur bering". - State qayerda yashashi — katta ilovalarda asosiy arxitektura savoli. Komponent ichida, otada, Context'da (Context I) yoki tashqi kutubxonada (keyingi qismlarda).
- Intervyu savollari: "Nega oddiy o'zgaruvchi ishlamaydi?", "
setStatedan keyin qiymat darhol o'zgaradimi?", "Lazy init nima?", "Ikki bir xil komponent state'ni bo'lishadimi?".
Xulosa
- State — chizishlar orasida saqlanadigan xotira:
const [x, setX] = useState(boshlang'ich). - Setter qiymatni React'ga beradi va qayta chizishni rejalashtiradi; komponent qayta chaqirilganda
useStateyangi qiymatni qaytaradi. - Oddiy
lethar chaqiruvda noldan boshlanadi va React'ga xabar bermaydi. - Har komponent nusxasining o'z state'i bor; state daraxtdagi o'ringa bog'langan va shaxsiy.
- Boshlang'ich qiymat faqat birinchi chizishda; og'ir hisob —
useState(() => …); TypeScript'dauseState<string | null>(null). vazifalarda forma xatosi va teg takliflari — state, atributlar undan chiziladi; Vitest 14/14.
Keyingi dars: State II: snapshot, batching va updater funksiya — nega setCount(count + 1) uch marta yozilsa ham son bittaga oshadi va buni qanday to'g'rilash.
Manbalar
- React: "State: A Component's Memory" — react.dev/learn/state-a-components-memory
- React:
useState— "Avoiding recreating the initial state" — react.dev/reference/react/useState - React: "Rules of Hooks" — react.dev/reference/rules/rules-of-hooks
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!