Mundarija (32)
- Bu darsda
- 1. Nega bu kerak?
- 2. Muammoni ko'ramiz
- 2.1 Har panelning o'z state'i
- 3. Uch qadamda ko'tarish
- 3.1 1-qadam: bolada state'ni olib tashlash
- 3.2 2-qadam: otada qattiq qiymat bilan sinash
- 3.3 3-qadam: state otada, setter — callback orqali
- 4. Callback props
- 4.1 Nomlash odati
- 4.2 Argument bilan xabar berish
- 4.3 Ma'lumot oqimi jadvali
- 5. Controlled va uncontrolled komponent
- 6. State qayerda turishi kerak?
- 6.1 Tartib
- 6.2 Keragidan baland emas
- 6.3 Yagona haqiqat manbai
- 7. Vazifalar qadami: bitta App ildizi
- 7.1 Oldin: bir nechta orol
- 7.2 Keyin: bitta ota
- 7.3 State qayerda?
- 7.4 Haqiqiy tuzoq: Qidiruv.tsx va qidiruv.ts
- 7.5 Tekshiruv va commit
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): Umumiy ota
- 2-mashq (o'rta): Stol xaritasi va bron qatori
- 3-mashq (qiyin): Savat sarlavhada, o'chirish ro'yxatda
- 4-mashq: Vazifalar qadami
- 10. Real ishda
- Xulosa
- Manbalar
State'ni ko'tarish: yagona haqiqat manbai va callback props
Qisqacha: Ikki komponent bir ma'lumotni ko'rishi yoki o'zgartirishi kerak bo'lsa, state ularning eng yaqin umumiy otasiga ko'tariladi. Ota qiymatni props bilan pastga beradi, bolalar esa o'zgarishni callback props (
onSelect,onAdd) orqali yuqoriga xabar qiladi: ma'lumot pastga oqadi, hodisa yuqoriga. Har ma'lumotning bitta egasi bo'ladi — yagona haqiqat manbai. O'z state'iga ega komponent — uncontrolled, hamma narsani props'dan oladigan komponent — controlled.
Bu darsda
- Ikki qo'shni komponent bir state'ni ulashishi kerakligini taniysiz.
- State'ni uch qadamda umumiy otaga ko'tarasiz.
- Callback props yozasiz va nomlaysiz (
onX/handleX). - Controlled va uncontrolled komponent farqini tushuntira olasiz.
- State qayerda turishini aniqlash tartibini qo'llaysiz: eng yaqin umumiy ota, lekin keragidan baland emas.
vazifalarning butun asosiy UI'si bittaAppostiga yig'iladi.
Oldin bilishingiz kerak: State strukturasini loyihalash, Props, Hodisalarni boshqarish.
1. Nega bu kerak?
Oldingi darsda bitta komponent ichidagi state'ni tartibga soldik: hosilaviy qiymat, id, status. Endi ikki komponent o'rtasidagi savol.
«Bahor» saytida "Ko'p so'raladigan savollar" bo'limi bor: "Ish vaqti", "Manzil", "Yetkazib berish". Har savol bosilsa, javobi ochiladi. Jasur akaning talabi: bir vaqtda faqat bitta javob ochiq tursin — telefon ekrani kichik. Sardor har savol uchun Panel komponenti yozdi, har birida o'z isOpen state'i. Natijada har panel o'zicha ochiladi va ikkinchisidan bexabar.
Muammo shundaki, "qaysi panel ochiq" degan ma'lumot birorta panelga tegishli emas. U hammasiga tegishli. Har panel o'z bo'lagini saqlasa, ularni kelishtiradigan joy yo'q.
Hayotiy o'xshatish: oshxonada bitta zal va uchta ofitsiant. Har ofitsiant "qaysi stol bo'sh" ni o'z daftariga yozsa, ikkitasi bir stolga mehmon o'tqazadi. To'g'ri yo'l — eshik oldidagi administrator: stollar ro'yxati unda. Ofitsiant "7-stolni bersam bo'ladimi?" deb so'raydi, administrator yozadi va hammaga aytadi.
React'da administrator — umumiy ota komponent. Bu usul state'ni ko'tarish (lifting state up) deyiladi.
2. Muammoni ko'ramiz
2.1 Har panelning o'z state'i
import { useState } from "react";
function Panel({ title, children }) {
const [isOpen, setIsOpen] = useState(false);
return (
<section>
<h3>
<button onClick={() => setIsOpen(!isOpen)}>{title}</button>
</h3>
{isOpen && <p>{children}</p>}
</section>
);
}
function App() {
return (
<>
<Panel title="Ish vaqti">Har kuni 07:00–23:00</Panel>
<Panel title="Manzil">Toshkent, Chilonzor</Panel>
</>
);
}Ekranda:
Ish vaqti
Har kuni 07:00–23:00
Manzil
Toshkent, ChilonzorIkkala javob ham ochiq. Har Panel nusxasining o'z xotirasi bor (useState darsida: "har nusxaning o'z state'i"). Birinchi panel ikkinchisining isOpen ini ko'rmaydi va o'zgartira olmaydi.
flowchart TD
A["App — state yo'q"] --> P1["Panel: isOpen = true"]
A --> P2["Panel: isOpen = true"]Ikki panel bir-biridan bexabar, App esa umuman hech narsa bilmaydi.
3. Uch qadamda ko'tarish
React hujjatidagi tartib uch qadamdan iborat. Har qadamda kod ishlaydi — faqat xulq o'zgaradi.
3.1 1-qadam: bolada state'ni olib tashlash
isOpen ni Panel dan olib tashlaymiz. Uning o'rniga Panel qiymatni props'dan oladi:
function Panel({ title, children, isOpen }) {
// const [isOpen, setIsOpen] = useState(false); ← olib tashlandiEndi Panel "ochiqmi?" degan savolga o'zi javob bermaydi — kim chizsa, o'sha aytadi.
3.2 2-qadam: otada qattiq qiymat bilan sinash
Ota hozircha qo'lda beradi: birinchisi ochiq, ikkinchisi yopiq.
function Panel({ title, children, isOpen }) {
return (
<section>
<h3>
<button>{title}</button>
</h3>
{isOpen && <p>{children}</p>}
</section>
);
}
function App() {
return (
<>
<Panel title="Ish vaqti" isOpen={true}>
Har kuni 07:00–23:00
</Panel>
<Panel title="Manzil" isOpen={false}>
Toshkent, Chilonzor
</Panel>
</>
);
}Ekranda:
Ish vaqti
Har kuni 07:00–23:00
ManzilisOpen={true} ni false ga almashtirib sinab ko'ring. Ekran faqat props'ga bog'liq — bu qadam to'g'ri bajarilganini tekshirish usuli. Tugmalar hozircha hech narsa qilmaydi.
3.3 3-qadam: state otada, setter — callback orqali
Endi App ga state qo'shamiz. U nimani eslab qolishi kerak? "Qaysi panel ochiq" — bitta qiymat: panel raqami yoki id. Oldingi darsdagi qoida: ikki boolean emas, bitta qiymat.
import { useState } from "react";
function Panel({ title, children, isOpen, onShow }) {
return (
<section>
<h3>
<button onClick={onShow}>{title}</button>
</h3>
{isOpen && <p>{children}</p>}
</section>
);
}
function App() {
const [openId, setOpenId] = useState(null);
return (
<>
<Panel
title="Ish vaqti"
isOpen={openId === "vaqt"}
onShow={() => setOpenId("vaqt")}
>
Har kuni 07:00–23:00
</Panel>
<Panel
title="Manzil"
isOpen={openId === "manzil"}
onShow={() => setOpenId("manzil")}
>
Toshkent, Chilonzor
</Panel>
</>
);
}Ekranda:
Ish vaqti
Manzil
Toshkent, ChilonzorEndi "Manzil" ochilganda "Ish vaqti" o'zi yopildi. Qadamma-qadam:
| Qadam | openId (App'da) |
1-panel isOpen |
2-panel isOpen |
|---|---|---|---|
| boshida | null |
false |
false |
| «Ish vaqti» | "vaqt" |
true |
false |
| «Manzil» | "manzil" |
false |
true |
Bola tugma bosilganini sezadi, lekin state'ni o'zgartirmaydi — u faqat otaga xabar beradi: onShow(). Ota esa state'ni yangilaydi va ikkala panelni yangi props bilan qayta chizadi.
flowchart TD
A["App: openId"] -- "isOpen (props)" --> P1["Panel"]
A -- "isOpen (props)" --> P2["Panel"]
P1 -. "onShow() — hodisa" .-> A
P2 -. "onShow() — hodisa" .-> AUzluksiz chiziq — ma'lumot pastga, punktir — hodisa yuqoriga. Bu React'ning asosiy oqimi: ma'lumot bir tomonga (one-way data flow) oqadi. Bola otaning state'ini to'g'ridan-to'g'ri o'zgartira olmaydi — faqat ota bergan funksiyani chaqiradi.
Tekshirib ko'ring:
PanelichidaonShowo'rnigaonShow()yozilsa (onClick={onShow()}), nima bo'ladi?
Javob
onShow() render paytida chaqiriladi: App state'ni o'zgartiradi, bu yangi render chaqiradi, u yana onShow() ni chaqiradi — cheksiz sikl. React uni to'xtatadi: Too many re-renders. React limits the number of renders to prevent an infinite loop. Funksiyaning o'zini bering: onClick={onShow} (Hodisalarni boshqarish darsidagi qoida).
4. Callback props
4.1 Nomlash odati
Callback prop — ota bolaga beradigan funksiya; bola uni biror hodisa bo'lganda chaqiradi. Hodisa ishlovchisi kabi, nomi on bilan boshlanadi: onShow, onAdd, onSelect. Ota tomonda bu funksiyani yasaydigan ichki funksiya ko'pincha handle bilan nomlanadi:
function App() {
function handleAdd(id) { // ota: "ishlovchi"
// …state'ni yangilash
}
return <MenuItem onAdd={handleAdd} />; // bola: "on…" — hodisa
}Bu majburiy qoida emas, odat. Lekin begona kodni o'qiganda yordam beradi: on… ni ko'rsangiz — "bu prop hodisa", handle… ni ko'rsangiz — "bu shu komponentdagi ishlovchi".
4.2 Argument bilan xabar berish
Callback qiymat ham uzata oladi. «Bahor» menyusida taomni savatga qo'shish tugmasi bola komponentda, savat esa sarlavhada — boshqa komponentda. Ikkalasi bir ma'lumotni (savatni) ko'radi, shuning uchun state ularning umumiy otasi — App da:
import { useState } from "react";
const MENU = [
{ id: 1, name: "Osh", price: 35000 },
{ id: 2, name: "Choy", price: 5000 },
];
function Header({ count, total }) {
return <h2>Savat: {count} ta, {total} so'm</h2>;
}
function MenuItem({ item, onAdd }) {
return (
<p>
{item.name} — {item.price}{" "}
<button onClick={() => onAdd(item.id)}>{item.name} +</button>
</p>
);
}
function App() {
const [cartIds, setCartIds] = useState([]); // qo'shilgan id'lar
const count = cartIds.length;
const total = cartIds.reduce(
(sum, id) => sum + MENU.find((m) => m.id === id).price,
0,
);
function handleAdd(id) {
setCartIds([...cartIds, id]);
}
return (
<>
<Header count={count} total={total} />
{MENU.map((item) => (
<MenuItem key={item.id} item={item} onAdd={handleAdd} />
))}
</>
);
}Ekranda:
Savat: 3 ta, 75000 so'm
Osh — 35000 Osh +
Choy — 5000 Choy +MenuItem savatdan bexabar — u faqat "menga onAdd berildi, bosilganda id'imni aytaman" deydi. Header ham bexabar — u faqat ikki son oladi. Savatning yagona egasi — App. count va total — hosilaviy qiymatlar.
onAdd={handleAdd} — funksiyaning o'zi uzatiladi. MenuItem ichida esa () => onAdd(item.id) — strelkali funksiya, chunki argument kerak.
4.3 Ma'lumot oqimi jadvali
| Kim | Nima biladi | Nima qila oladi |
|---|---|---|
App |
savat (cartIds) |
state'ni yangilash |
Header |
count, total (props) |
faqat ko'rsatish |
MenuItem |
item, onAdd (props) |
otaga "qo'shish bosildi" deb aytish |
Bolalar ota haqida hech narsa bilmaydi va uning state nomini ham bilmaydi. Shuning uchun MenuItem ni boshqa joyda (masalan, "Tavsiyalar" bo'limida) qayta ishlatish oson: kim ishlatsa, onAdd ni o'zi beradi.
5. Controlled va uncontrolled komponent
Birinchi Panel (o'z isOpen state'i bilan) va oxirgi Panel (props'dan isOpen oladigan) — bir xil ko'rinadi, lekin turi boshqa:
| Uncontrolled | Controlled | |
|---|---|---|
| Asosiy qiymat qayerda | komponentning o'z state'ida | otada, props bilan keladi |
| Ota ta'sir qila oladimi | yo'q | ha, to'liq |
| Ishlatish | oson: <Panel title="…"> |
ko'proq yozuv: isOpen, onShow |
| Qachon tanlanadi | qiymat boshqalarga kerak emas | qiymatni boshqalar bilan kelishtirish kerak |
Uncontrolled komponent — muhim ma'lumotini o'zi saqlaydi. Controlled komponent — muhim ma'lumoti props bilan boshqariladi, o'zgarishni callback bilan xabar qiladi. Bu so'zlar formadagi <input> dan kelgan. Formalar I darsida input'ning controlled shaklini, Formalar II da uncontrolled shaklini ko'ramiz — g'oya aynan shu.
Komponent ikkalasini birga qo'llashi ham mumkin: ota isOpen bersa — controlled, bermasa — o'zi eslab qoladi. Bunday komponent API'sini loyihalash — 18-qismda.
6. State qayerda turishi kerak?
6.1 Tartib
Har state uchun quyidagi savollar:
- Bu state'ga asoslanib nimani chizadigan komponentlarni toping.
- Ularning eng yaqin umumiy otasini toping (daraxtda ikkalasidan yuqoridagi birinchi komponent).
- State'ni o'sha otaga qo'ying. Mos ota bo'lmasa — faqat shu state'ni saqlash uchun yangi komponent yarating.
«Bahor» menyu sahifasini tasavvur qiling:
flowchart TD
App --> Header["Header — savat soni"]
App --> Page["MenuPage"]
Page --> Filter["CategoryFilter"]
Page --> List["MenuList"]
List --> Item["MenuItem — qo'shish"]- Tanlangan kategoriya: uni
CategoryFilter(qaysi tugma tanlangan) vaMenuList(nimani ko'rsatish) ishlatadi. Umumiy ota —MenuPage. State —MenuPageda. - Savat: uni
Header(son) vaMenuItem(qo'shish) ishlatadi. Umumiy ota —App. State —Appda.
6.2 Keragidan baland emas
Hamma state'ni App ga ko'tarib qo'yish ham xato. Masalan, "Ish vaqti" panelining ochiqligi faqat FAQ bo'limiga kerak. Uni App da saqlasangiz, uch muammo paydo bo'ladi:
Appdagi har o'zgarish butun sahifani qayta chizadi (qayta render narxi — 18-qismda);- state'ni FAQ'gacha bir necha qavat props bilan "tashish" kerak bo'ladi — buni props drilling deyishadi, yechimi — Context;
Apphamma narsani biladigan "xudo-komponent"ga aylanadi.
Qoida: state'ni uni ishlatadigan komponentlarga iloji boricha yaqin saqlang. Ko'tarish faqat ulashish kerak bo'lganda. Bunga colocation ("yonma-yon joylash") deyiladi — Kichik React loyihani tashkil qilish darsida fayllar uchun ham shu g'oyani ko'ramiz.
6.3 Yagona haqiqat manbai
Har noyob ma'lumot uchun bitta komponent bor — u state'ni saqlaydi va yangilaydi. Boshqalar faqat props'dan o'qiydi. Bu yagona haqiqat manbai (single source of truth). U oldingi darsdagi qoidaning davomi: bitta komponent ichida "takrorlamang", komponentlar orasida esa "bitta egasi bo'lsin".
Ikki joyda saqlangan bir ma'lumot har doim bir-biridan ajralib qolish xavfida — Sardorning jami narxi, eskirib qolgan tanlangan taom, ikki ochiq panel. Hammasi bitta kasallik.
Tekshirib ko'ring: Kategoriya filtri
CategoryFiltervaMenuListda alohida state bo'lsa, qanday xato chiqishi mumkin?
Javob
Filtr tugmasi "Ichimliklar" ni tanlangan ko'rsatadi, ro'yxat esa hali "Hammasi" ni chizadi — ikkala state'ni bir xil yangilash kerak, birini unutish kifoya. Bundan tashqari, CategoryFilter o'z state'ini MenuList ga yetkazishning to'g'ri yo'li yo'q: qo'shni komponentlar bir-biriga to'g'ridan-to'g'ri ma'lumot bera olmaydi. Yechim — state MenuPage da, ikkalasiga props.
7. Vazifalar qadami: bitta App ildizi
7.1 Oldin: bir nechta orol
Hozirgi vazifalar da (State III darsidagi holat) React bir nechta joyda alohida ishlaydi. Yangi vazifa formasi — o'z ildizi (createRoot(formaJoyi)), ro'yxat qatorlari — o'z ildizi (createRoot(royxat)). Qidiruv, filtr tugmalari, hisob, teglar, aloqa qatori, o'chirish dialogi esa hali index.html da va ularni render.tsx dagi vanilla funksiyalar yangilaydi. Bu orollar bir-biridan bexabar.
Natija — bugungi darsning muammosi kichik masshtabda. Masalan, formadagi teg takliflari (#ish yozilganda "ishxona" taklifi) teglar ro'yxatiga bog'liq. Teglar daraxti esa teglarniYoz() ichida, boshqa joyda quriladi. Forma ildizi bir marta chiziladi va teglar o'zgarganini o'zi bilmaydi.
7.2 Keyin: bitta ota
index.html dagi uzun blok bitta <div> ga aylanadi (refactor/app-ildizi branch'ida davom etamiz, oldingi darsda ochilgan):
<main>
<h1>Vazifalar</h1>
- <p id="aloqa" role="status"></p>
- <button type="button" id="qayta-tugma"
- hidden>Qayta urinish</button>
- <!-- Yangi vazifa formasi — React (17/#12, YangiVazifaForma.tsx) -->
- <div id="yangi-forma-joyi"></div>
- <search>
…
- <p id="hisob" aria-live="polite"></p>
- <p id="teglar" hidden></p>
- <p id="elon" role="status"></p>
+ <!-- React ildizi (17/#16): aloqa, forma, qidiruv, filtr, ro'yxat,
+ hisob, e'lon va o'chirish dialogi — src/App.tsx -->
+ <div id="ilova"></div>
<h2>Eksport</h2>(… — 25 ta olib tashlangan qator qisqartirildi: qidiruv, filtrlar, saralash, ro'yxat.) Eksport va import hali vanilla — ular Formalar II darsida ko'chadi.
Yangi src/App.tsx ning asosiy qismi (kanondan; importlar va boshMatnlar qisqartirildi):
/** App'dan chiqadigan hodisalar — ularni asosiy.ts bajaradi */
export type IlovaAmallari = {
qator: QatorAmallari;
xatoniTop: (matn: string) => string | null;
onYoz: (matn: string) => void;
onQosh: (matn: string) => QoshishNatijasi;
onQidir: (matn: string) => void;
onFiltr: (filtr: Filtr) => void;
onSaralash: (qiymat: string) => void;
onQaytaUrin: () => void;
};
type Props = {
holat: Readonly<Holat>;
qoralama: string; // forma ochilgandagi matn
amallar: IlovaAmallari;
};
export function App({ holat, qoralama, amallar }: Props) {
const { vazifalar, filtr, saralash, qidiruv } = holat;
// … hosilaviy qiymatlar (17/#15): royxat, korinadigan, qoldi,
// bajarildi, teglar, daraxt
return (
<>
<Aloqa tarmoq={holat.tarmoq} onQaytaUrin={amallar.onQaytaUrin} />
<YangiVazifaForma
boshlangichMatn={qoralama}
xatoniTop={amallar.xatoniTop}
takliflarniTop={(joriy) => tegTakliflari(daraxt, joriy)}
onYoz={amallar.onYoz}
onQosh={amallar.onQosh}
/>
<QidiruvMaydoni
qidiruv={qidiruv}
topildi={korinadigan.length}
onQidir={amallar.onQidir}
/>
<Filtrlar filtr={filtr} onFiltr={amallar.onFiltr} />
{/* … SaralashTanlovi, sarlavha, ro'yxat, bosh xabar */}
<Statistika
qoldi={qoldi}
bajarildi={bajarildi}
teglar={teglar}
yopilgan={royxat.yopilganTeglar()}
/>
<p id="elon" role="status">
{holat.elon}
</p>
<OchirishDialog matn={holat.ochirilayotgan} />
</>
);
}Bugungi darsni shu kodda toping:
- Callback props:
IlovaAmallari—Appdan chiqadigan hodisalar ro'yxati, har birion….FiltrlarfaqatonFiltr(f)ni chaqiradi — filtrni o'zi o'zgartirmaydi. - Yagona manba:
takliflarniTop={(joriy) => tegTakliflari(daraxt, joriy)}— forma takliflari endiApphisoblagan o'shadaraxtdan. Teglar o'zgarsa,Appqayta chiziladi va forma yangi funksiyani oladi. State III darsida knip topgan ortiqcha eksport —tegTakliflari— shu yerdateglar.tsga ko'chdi va endi haqiqatan import qilinadi: knip toza. - Controlled komponentlar:
QidiruvMaydonivaSaralashTanlovimaydonining qiymati props'dan (value={qidiruv}). Do'kon qidiruvni tozalasa (vazifa qo'shilganda), maydon ham bo'shaydi. Input'ning bu shakli — Formalar I darsida batafsil.
Filtrlar.tsx — eng qisqa controlled komponent (kanondan aynan):
export function Filtrlar({ filtr, onFiltr }: Props) {
return (
<div id="filtrlar" role="group" aria-label="Filtr">
{FILTRLAR.map((f) => (
<button
key={f}
type="button"
data-filtr={f}
aria-pressed={f === filtr}
onClick={() => onFiltr(f)}
>
{NOMLAR[f]}
</button>
))}
</div>
);
}aria-pressed — filtr dan hisoblanadi. Avval vanilla filtrTugmalariniYoz() har tugmani aylanib setAttribute qilardi — endi bu kod yo'q.
7.3 State qayerda?
Diqqat qiling: App da birorta ham useState yo'q. Haqiqat manbai hozircha vanilla do'kon — holat.ts dagi holat obyekti (State III da u o'zgarmas massivlarga o'tgan). render.tsx endi ko'prik:
export function render(): void {
if (ulangan === null) {
return;
}
const { qoralama, amallar } = ulangan;
// root.render() navbatga qo'yadi, DOM keyinroq yangilanadi. flushSync —
// hozir, shu qatorda: keyingi kod (fokus, dialog) tayyor DOM'ga
// qaraydi; birinchi chizish ham birinchi kadrga ulguradi (CLS 0).
// {...holat} — nusxa: React'ga o'zgarmaydigan surat beramiz
flushSync(() => {
ildiz.render(
<StrictMode>
<App
holat={{ ...holat }}
qoralama={qoralama}
amallar={amallar}
/>
</StrictMode>,
);
});
}Ya'ni App — butunlay controlled komponent: hamma narsa props'dan, hamma o'zgarish callback orqali asosiy.ts ga. Do'kon o'zgarsa, render() App ni yangi props bilan qayta chizadi. Bu strangler ("asta-sekin almashtirish") usuli: eski kod bo'lakma-bo'lak yangisi bilan almashadi va ilova biror qadamda ham ishlamay qolmaydi. Haqiqat manbai effektlar o'tilgach App ning o'z state'iga ko'chadi (useEffect III darsida).
asosiy.ts callback'larni beradi (parcha):
ilovaniUla(qoralamaniOl(), {
qator: {
onAlmashtir(id) {
elonQil("");
holat.tahrirId = null;
katakniAlmashtir(id, qoshniId(id));
},
// …
},
onQidir: qidiruvniYangila,
// …
});7.4 Haqiqiy tuzoq: Qidiruv.tsx va qidiruv.ts
Kanonni yozishda qidiruv komponenti birinchi bo'lib Qidiruv.tsx deb nomlangan edi. src/ da esa allaqachon qidiruv.ts (normallash funksiyalari) bor. Windows'da fayl nomi katta-kichik harfni farqlamaydi. ESLint shunday yiqildi:
Parsing error: …\src\Qidiruv.tsx was not found by the project service"Tahlil xatosi: Qidiruv.tsx loyiha xizmati tomonidan topilmadi." TypeScript ikki nomni bitta fayl deb ko'rdi. Linux'da (CI, Vercel) esa ular ikki xil fayl — xato boshqa joyda, boshqacha ko'rinishda chiqishi mumkin edi. Tuzatish — farqli nom: QidiruvMaydoni.tsx, SaralashTanlovi.tsx. Qoida: komponent fayli nomi bir papkadagi boshqa fayldan faqat harf katta-kichikligi bilan farq qilmasin.
7.5 Tekshiruv va commit
npm run build && npm test && npm run lint && npm run tipKanon natijalari: node:test — 149/149, Vitest — 18 (4 ta yangi App testi: "hisob va teglar ro'yxatdan hisoblanadi", "filtr: tanlangani aria-pressed, bosilsa — onFiltr" va boshqalar), brauzer tekshiruvi — 159/159. Diff: 13 fayl, +583 −349.
Commit:
refactor: bitta App ildizi — hosilaviy qiymatlar renderdaBody: "forma, qidiruv, filtr, saralash, ro'yxat, hisob, teglar, aloqa, e'lon, dialog — App.tsx ostida; ma'lumot — do'kondan props bilan. render.tsx — ko'prik". PR'ni birlashtiring.
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| qo'shni komponentlar bir-biriga mos kelmaydi | har birida o'z state nusxasi | state'ni umumiy otaga ko'taring |
| bola otaning state'ini o'zgartira olmaydi | setter bolaga berilmagan | onX callback prop bering |
Too many re-renders |
onClick={onShow()} |
onClick={onShow} yoki () => onShow(id) |
App juda katta, har harf butun sahifani chizadi |
hamma state ildizda | state'ni ishlatuvchilarga yaqin saqlang |
onAdd is not a function |
ota prop'ni bermagan yoki nomi boshqa | prop nomini tekshiring, TS'da tur yozing |
Oxirgi xato jonli:
function MenuItem({ name, onAdd }) {
return <button onClick={() => onAdd(name)}>{name} qo'shish</button>;
}
function App() {
return <MenuItem name="Osh" onAd={() => {}} />;
}Konsolda:
Uncaught TypeError: onAdd is not a function
TypeError: onAdd is not a functionNatija oynasining konsoli xatoni ikki qatorda ko'rsatadi, matn bir xil. Ota onAd (harf tushib qolgan) berdi, bola esa onAdd ni kutdi — undefined ni chaqirib bo'lmaydi. Xato bosilganda chiqadi, render paytida emas. TypeScript'da props turi yozilgan bo'lsa, bu xato kompilyatsiyada ushlanadi.
9. Mashqlar
Mashqlar kurs/mashqlar/17/16-state-kotarish/ da.
1-mashq (oson): Umumiy ota
Daraxt: App → (Header, MenuPage); MenuPage → (CategoryFilter, MenuList); MenuList → MenuItem. Header da "Sevimlilar: 2" yozuvi bor, MenuItem da esa "" tugmasi.
- Sevimlilar ro'yxati qaysi komponentda turishi kerak:
MenuItemsevimliga qo'shishni otaga qaysi prop orqali bildiradi (nom odatiga ko'ra, masalan):- Faqat
MenuListishlatadigan "saralash" tanlovi qayerda tursa yetadi:
Yechim
Sevimlilarni Header va MenuItem ishlatadi — eng yaqin umumiy ota App. Callback nomi on bilan boshlanadi: onToggleFavorite (yoki onFavorite — muhimi on…). Saralash faqat MenuList ga kerak — uni ko'tarish shart emas (colocation).
2-mashq (o'rta): Stol xaritasi va bron qatori
Ikki qo'shni komponent: TableMap (stollar tugmalari: 1, 2, 3) va BookingSummary ("Tanlangan stol: 2" yoki "Stol tanlanmagan"). Hozir TableMap tanlovni o'z state'ida saqlaydi va BookingSummary uni ko'rmaydi. State'ni App ga ko'taring. TableMap — controlled: selected va onSelect props'ini oladi; tanlangan tugma aria-pressed bilan belgilansin.
Yechim
import { useState } from "react";
function TableMap({ selected, onSelect }) {
return (
<div>
{[1, 2, 3].map((n) => (
<button
key={n}
aria-pressed={n === selected}
onClick={() => onSelect(n)}
>
{n}-stol
</button>
))}
</div>
);
}
function BookingSummary({ table }) {
return (
<p>
{table === null
? "Stol tanlanmagan"
: `Tanlangan stol: ${table}`}
</p>
);
}
function App() {
const [table, setTable] = useState(null);
return (
<>
<TableMap selected={table} onSelect={setTable} />
<BookingSummary table={table} />
</>
);
}Ekranda:
1-stol2-stol3-stol
Tanlangan stol: 2onSelect={setTable} — setter'ning o'zi callback bo'la oladi: u argument oladi va state'ni yangilaydi. TableMap buni bilmaydi — u faqat "bosilganda onSelect(n)" deydi.
3-mashq (qiyin): Savat sarlavhada, o'chirish ro'yxatda
TypeScript'da uch komponent: Header ("Savat: N ta, S so'm"), Menu (har taomga "qo'shish") va Cart (savatdagi har qator va "olib tashlash" tugmasi). Savat — App da, faqat id'lar massivi (bir taom bir necha marta bo'lishi mumkin). Talablar:
- props turlari:
onAdd: (id: number) => void,onRemove: (index: number) => void; countvatotal— hosilaviy;- "O'chir" tugmasi massivdagi o'sha o'rindagi elementni olib tashlaydi (
filterikkinchi argumenti — indeks;filter).
Menyu: osh 35 000, lag'mon 28 000. Sinov: osh, lag'mon, osh qo'shing va savatdagi birinchi qatorni olib tashlang.
Yechim
import { useState } from "react";
type Dish = { id: number; name: string; price: number };
const MENU: Dish[] = [
{ id: 1, name: "Osh", price: 35000 },
{ id: 2, name: "Lag'mon", price: 28000 },
];
function findDish(id: number): Dish {
const dish = MENU.find((d) => d.id === id);
if (dish === undefined) throw new Error(`Taom yo'q: ${id}`);
return dish;
}
function Header({ count, total }: { count: number; total: number }) {
return <h2>Savat: {count} ta, {total} so'm</h2>;
}
function Menu({ onAdd }: { onAdd: (id: number) => void }) {
return (
<p>
{MENU.map((d) => (
<button key={d.id} onClick={() => onAdd(d.id)}>
{d.name} +
</button>
))}
</p>
);
}
type CartProps = {
ids: readonly number[];
onRemove: (index: number) => void;
};
function Cart({ ids, onRemove }: CartProps) {
return (
<ol>
{ids.map((id, i) => (
<li key={i}>
{findDish(id).name}{" "}
<button onClick={() => onRemove(i)}>O'chir {i + 1}</button>
</li>
))}
</ol>
);
}
export default function App() {
const [ids, setIds] = useState<number[]>([]);
const total = ids.reduce((sum, id) => sum + findDish(id).price, 0);
return (
<>
<Header count={ids.length} total={total} />
<Menu onAdd={(id) => setIds([...ids, id])} />
<Cart
ids={ids}
onRemove={(index) =>
setIds(ids.filter((_, i) => i !== index))
}
/>
</>
);
}Ekranda:
Savat: 2 ta, 63000 so'm
Osh +Lag'mon +
Lag'mon O'chir 1
Osh O'chir 2key={i} — bu yerda indeks kalit ataylab: qatorlarda o'z state'i yo'q va bir xil taom ikki marta bo'lishi mumkin, id noyob emas. Qatorlarda state bo'lsa, indeks kalit xavfli — buni keyingi darsda ko'ramiz. findDish topilmagan id'da xato tashlaydi — undefined.price dan ko'ra aniq xabar.
4-mashq: Vazifalar qadami
refactor/app-ildizi branch'ida «Vazifalar qadami: bitta App ildizi» bo'limidagi o'zgarishlarni kiriting: App.tsx, Aloqa.tsx, Filtrlar.tsx, QidiruvMaydoni.tsx, SaralashTanlovi.tsx, Statistika.tsx (oldingi darsdan), OchirishDialog.tsx, teglar.ts, render.tsx ko'prigi va index.html. Keyin tekshiruvlar va commit.
Yechim
Tekshiruv ro'yxati:
npm run lint—was not found by the project servicechiqsa,src/da faqat harf katta-kichikligi bilan farqlanadigan fayl nomlarini qidiring.npm run knip—render.tsxdan olib tashlangan funksiyalar (hisobniYoz,matnniYoz) boshqa joyda qolmaganini ko'rsatadi.npm test— node 149, Vitest 18.- Brauzerda: filtr tugmasini bosing —
aria-pressedDevTools Elements'da almashadi; qidiruv maydoniga yozing — "N ta topildi" yangilanadi.
Yakuniy kod — refactor: bitta App ildizi — hosilaviy qiymatlar renderda commit'i. PR'ni birlashtiring.
10. Real ishda
- Har kungi ish: "bu ma'lumot kimga tegishli?" — React'da eng ko'p beriladigan arxitektura savoli. Kod ko'rigida "bu state ikki joyda — ko'taring" yoki aksincha "bu state App'da nima qilyapti? — pastga tushiring" izohlari tez-tez uchraydi.
- UI kutubxonalar (Radix, MUI, shadcn/ui) komponentlarida
value/onValueChangevadefaultValuejuftligi — controlled va uncontrolled rejim bir komponentda. Nomlari shu darsdagi odatga mos. - Katta ilovalarda ko'tarish bir necha qavatga yetganda Context (Context I) yoki state kutubxonasi (Zustand, Redux — 19-qism) ishlatiladi. Lekin ular ham "yagona haqiqat manbai" g'oyasining boshqa ko'rinishi.
- Intervyu savollari: "Lifting state up nima?", "Qo'shni komponentlar qanday ma'lumot almashadi?", "Controlled va uncontrolled komponent farqi?", "One-way data flow nima?".
Xulosa
- Ikki komponent bir ma'lumotga muhtoj bo'lsa — state eng yaqin umumiy otaga ko'tariladi.
- Ota qiymatni props bilan beradi, bola o'zgarishni
on…callback bilan so'raydi: ma'lumot pastga, hodisa yuqoriga. - Controlled komponent — qiymat props'dan; uncontrolled — o'z state'idan.
- State'ni keragidan baland ko'tarmang: uni ishlatuvchilarga yaqin saqlang.
- Har ma'lumotning bitta egasi — yagona haqiqat manbai; ikki nusxa — xato manbai.
vazifalarning asosiy UI'si bittaAppostida;App— controlled, haqiqat hozircha vanilla do'konda.
Keyingi dars: State'ning saqlanishi va key bilan reset — state komponentga emas, daraxtdagi o'rniga bog'langan: qachon u saqlanadi, qachon yo'qoladi va key bilan uni qanday ataylab noldan boshlash mumkin.
Manbalar
- React: "Sharing State Between Components" — react.dev/learn/sharing-state-between-components
- React: "Thinking in React" (4-qadam: state qayerda yashaydi) — react.dev/learn/thinking-in-react
- React: "Responding to Events" (hodisa props nomlari) — react.dev/learn/responding-to-events
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!