Mundarija (35)
- Bu darsda
- 1. Nega bu kerak?
- 2. Sinxron render: React to'xtay olmaydi
- 2.1 Ataylab sekin komponent
- 2.2 Tab almashtirish — oddiy usulda
- 2.3 Vaqt chizig'ida
- 3. Concurrent rendering: render bo'laklarga bo'linadi
- 3.1 Ikki xil yangilanish
- 3.2 Bo'laklar va navbat
- 3.3 To'xtatilgan render — vaqt chizig'ida
- 4. useTransition
- 4.1 Tab almashtirish — transition bilan
- 4.2 isPending — "yuklanmoqda" ko'rsatkichi
- 4.3 startTransition — hooksiz
- 5. Qidiruv: maydon shoshilinch, ro'yxat — transition
- 5.1 Ikki state
- 5.2 memo siz — transition yordam bermaydi
- 5.3 Ikki state'ning narxi
- 6. Async transition (React 19)
- 6.1 Serverga saqlash
- 6.2 Tuzoq: await dan keyingi setter
- 7. Cheklovlar
- 8. Vazifalar qadami: useTransition varianti va o'lchov
- 8.1 Vazifa: 10 000 vazifada qidiruv
- 8.2 Variant: ikki state va transition
- 8.3 O'lchov
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): shoshilinchmi yoki transition?
- 2-mashq (o'rta): «Bahor» boshqaruv paneli
- 3-mashq (qiyin): buyurtma yuborish — async transition
- 4-mashq: vazifalar ga nazar
- 11. Real ishda
- Xulosa
- Manbalar
Concurrent rendering va useTransition: og'ir yangilanishni to'xtatib bo'ladigan qilish
Qisqacha: Oddiy holatda React renderni boshlasa, oxirigacha to'xtamaydi — og'ir ro'yxat chizilayotganda bosish va yozish kutib turadi. Concurrent rendering — React renderni bo'laklarga bo'lib, orada brauzerga navbat berishi va kerak bo'lsa boshlangan renderni tashlab yuborishi. Buning uchun yangilanishni transition (shoshilinch emas) deb belgilaysiz:
const [isPending, startTransition] = useTransition(), keyinstartTransition(() => setTab("taomlar")). Maydon va tugma darhol javob beradi, og'ir qism esa fonda chiziladi.vazifalarda 10 000 vazifa bilan qidiruvda eng uzun javob 1 184 ms dan 168 ms ga tushdi.
Bu darsda
- Sinxron render nega ekranni "qotiradi"ni konsoldagi loglar bilan ko'rasiz.
- Shoshilinch (urgent) va transition yangilanishni farqlay olasiz.
- To'xtatilgan render qanday ko'rinishini vaqt chizig'ida o'qiysiz.
useTransition,isPendingvastartTransitionbilan tab almashtirish va qidiruvni yozasiz.- React 19 dagi async transition'ni va uning tuzog'ini (
awaitdan keyingi setter) bilasiz.
Oldin bilishingiz kerak: Pure komponentlar va render qoidasi, State II: snapshot, batching va updater funksiya, useEffect I, React ilovani debug qilish.
1. Nega bu kerak?
17-qism debug darsi bilan tugadi: vazifalar v7 butunlay React'da. 1 000 vazifada hammasi tez edi. Endi Jasur aka «Bahor»ning butun yillik ro'yxatini — 10 000 vazifani — yukladi va qidiruvga "vazifa 12" deb yozdi.
Har harfdan keyin maydon bir soniyaga yaqin qotib qoldi. Biz buni Chrome'da haqiqiy klaviatura bilan o'lchadik: eng uzun javob — 1 184 ms, shundan 1 024 ms — bitta harfga React'ning ishi. Odam 100 ms dan uzun kutishni "sekin" deb sezadi. Bir soniya — "ilova buzildi".
Nega? Har harf qidiruv state'ini o'zgartiradi. React App ni qayta chizadi va 10 000 qatordan iborat ro'yxatni yangi so'rov bilan hisoblaydi. Bu ish boshlangan zahoti oxirigacha bajariladi. Shu vaqt ichida brauzer keyingi harfni maydonga yoza olmaydi — JavaScript bitta oqimda ishlaydi (Event loop).
Hayotiy o'xshatish: oshpaz 200 porsiya osh damlayapti. Mijoz "bitta choy bering" desa, oddiy oshpaz oshni tugatmaguncha qaramaydi. Tajribali oshpaz esa har bir necha daqiqada bosh ko'taradi: choyni darhol uzatadi va oshga qaytadi. Agar buyurtma o'zgarib, "osh emas, manti" deyishsa — boshlangan oshni tashlab, mantiga o'tadi. React'ning concurrent renderingi — shu tajribali oshpaz.
2. Sinxron render: React to'xtay olmaydi
2.1 Ataylab sekin komponent
Sekinlikni ko'rish uchun 10 000 vazifa shart emas. Bitta "og'ir" komponent yasaymiz. slowWork() — 20 million marta qo'shish bajaradigan oddiy sikl. U vaqtni o'lchamaydi, faqat sanaydi. Shuning uchun natija har kompyuterda bir xil, faqat tezligi farq qiladi: bizning kompyuterda (Intel Core i5-12500H, Node 24) bitta chaqiruv 50–90 ms oldi, telefonda bir necha barobar ko'proq.
function slowWork() {
let x = 0;
for (let i = 0; i < 20_000_000; i++) x += i % 7;
return x;
}20_000_000 dagi _ — sonni o'qishni osonlashtiradigan ajratgich (Son yozish shakllari darsida ko'rgansiz), qiymati 20000000. Bu funksiyani komponent ichida chaqirsak, o'sha komponentning har chizilishi 50 ms dan ortiq davom etadi. Xuddi haqiqiy ilovadagi og'ir qator yoki murakkab diagramma kabi.
2.2 Tab almashtirish — oddiy usulda
«Bahor» sayti: uchta tab — bosh sahifa, "Taomlar" (8 ta og'ir karta) va "Aloqa". Mehmon "Taomlar" ni bosdi, keyin fikridan qaytib, darhol "Aloqa" ni bosdi. Har tab ekranga chiqqanda effekt konsolga yozadi — effekt faqat commitdan keyin ishlaydi (useEffect I), demak bu yozuv "tab haqiqatan ekranga chiqdi" degani:
import { useEffect, useState } from "react";
function slowWork() {
let x = 0;
for (let i = 0; i < 20_000_000; i++) x += i % 7;
return x;
}
function Dish({ n }) {
slowWork(); // ataylab sekin: 50 ms dan ortiq
return <li>Taom {n}</li>;
}
function MenuTab() {
useEffect(() => console.log("ekranga chiqdi: Taomlar"), []);
const items = [];
for (let i = 1; i <= 8; i++) items.push(<Dish key={i} n={i} />);
return <ul>{items}</ul>;
}
function ContactTab() {
useEffect(() => console.log("ekranga chiqdi: Aloqa"), []);
return <p>Tel: +998 90 000 00 00</p>;
}
function App() {
const [tab, setTab] = useState("bosh");
console.log("App:", tab);
return (
<div>
<button onClick={() => setTab("taomlar")}>Taomlar</button>
<button onClick={() => setTab("aloqa")}>Aloqa</button>
{tab === "bosh" && <p>Xush kelibsiz</p>}
{tab === "taomlar" && <MenuTab />}
{tab === "aloqa" && <ContactTab />}
</div>
);
}Konsolda:
App: bosh
App: taomlar
ekranga chiqdi: Taomlar
App: aloqa
ekranga chiqdi: AloqaEkranda:
TaomlarAloqa
Tel: +998 90 000 00 00Mehmonga "Taomlar" kerak emas edi, lekin u baribir to'liq chizildi va ekranga chiqdi. "Aloqa" bosilishi esa shu ish tugaguncha navbatda turdi. «Ishga tushir» ni bosib, ikki tugmani tez-tez bosib ko'ring: "Aloqa" bir lahza kechikib ochiladi, sekin kompyuterda bu sezilarli.
2.3 Vaqt chizig'ida
Nima bo'lganini millisekundlarda yozamiz. Sakkizta karta × ~50 ms ≈ 400 ms (sxema, raqamlar yaxlitlangan):
| Vaqt | Nima bo'ldi | Brauzer |
|---|---|---|
| 0 ms | "Taomlar" bosildi → setTab("taomlar") |
— |
| 0–400 ms | React 8 ta kartani ketma-ket chizadi | band, hech narsa qila olmaydi |
| 100 ms | mehmon "Aloqa" ni bosdi | bosish navbatda kutadi |
| 400 ms | commit: "Taomlar" ekranda | endi bo'shadi |
| 400 ms | bosish ishlov oladi → setTab("aloqa") |
— |
| ~401 ms | "Aloqa" ekranda | — |
Asosiy muammo — ikkinchi qator. Render boshlandimi — React uni oxirigacha olib boradi. Bu sinxron render: React 17 va undan oldin hamma render shunday edi, hozir ham oddiy setTab shunday ishlaydi.
Tekshirib ko'ring: Yuqoridagi misolda mehmon "Taomlar" ni bosgandan 100 ms keyin "Aloqa" ni bosdi. Nega konsolda "ekranga chiqdi: Taomlar" baribir bor?
Javob
Chunki "Aloqa" bosilishi JavaScript bo'shaganidan keyingina ishlov oldi. U paytda "Taomlar" renderi allaqachon tugab, commit qilingan edi. Sinxron renderni hech narsa to'xtata olmaydi — hatto keraksiz bo'lib qolgan bo'lsa ham.
3. Concurrent rendering: render bo'laklarga bo'linadi
3.1 Ikki xil yangilanish
Har yangilanish bir xil muhim emas. React ularni ikkiga ajratadi:
| Shoshilinch (urgent) | Transition | |
|---|---|---|
| Misol | harf yozish, tugma bosish, katakni belgilash | tab mazmuni, qidiruv natijasi, grafik |
| Foydalanuvchi kutadi | darhol (≤ 100 ms) | biroz kechiksa ham bo'ladi |
| Render | to'xtatilmaydi | bo'laklarga bo'linadi, to'xtatiladi |
| Kim belgilaydi | sukut bo'yicha hamma set… |
siz — startTransition bilan |
Shoshilinch yangilanish (urgent update) — foydalanuvchi o'z harakatining natijasini darhol kutadigan yangilanish. Harfni yozdingiz — u maydonda shu zahoti ko'rinishi kerak. Transition (o'tish) — ekranni bir ko'rinishdan boshqasiga o'tkazadigan yangilanish. Yangi tab mazmuni 200 ms keyin chiqsa, hech kim sezmaydi. Lekin bosish 200 ms javobsiz qolsa, hamma sezadi.
3.2 Bo'laklar va navbat
Concurrent rendering (navbatma-navbat chizish; "parallel" emas — JavaScript baribir bitta oqimda) — React transition renderini bir urinishda emas, kichik bo'laklarda bajarishi. Har bo'lakdan keyin u brauzerdan so'raydi: "kutib turgan muhimroq ish bormi?" React 19.3.0 bilan keladigan scheduler paketida (0.28.0) bo'lak uzunligi — 5 ms (frameInterval = 5). Bo'lak tugadi va bosish kutib turibdi — React to'xtaydi, bosishni o'tkazadi, keyin transition'ga qaytadi.
Qaytganda ikki yo'l bor. Yangi bosish transition natijasiga ta'sir qilmagan bo'lsa — React to'xtagan joyidan davom etadi. Ta'sir qilgan bo'lsa (masalan, "Aloqa" bosildi va "Taomlar" endi kerak emas) — boshlangan render tashlab yuboriladi. React yangi holat bilan qaytadan boshlaydi.
Bu xavfsizmi? Ha, chunki render bosqichi faqat hisob-kitob. Pure komponentlar darsidagi qoida aynan shu uchun kerak edi: render DOM'ga tegmaydi, tashqi narsani o'zgartirmaydi. Tashlab yuborilgan render hech qanday iz qoldirmaydi. Commit esa doim bir martada, oxirigacha bajariladi — ekranda yarim chizilgan tab hech qachon ko'rinmaydi.
flowchart TD
A["setTab: transition"] --> B["5 ms bo'lak chizildi"]
B --> C{"Navbatda bosish bormi?"}
C -->|"yo'q"| D{"Render tugadimi?"}
D -->|"yo'q"| B
D -->|"ha"| E["Commit: ekranga"]
C -->|"ha"| F["Bosishni bajar"]
F --> G{"Transition hali kerakmi?"}
G -->|"ha"| B
G -->|"yo'q"| H["Tashla, yangi holat bilan boshla"]Diagrammaning o'ng tomoniga qarang: "Tashla" faqat render bosqichida mumkin. Commit'ga yetgan ish endi to'xtatilmaydi.
Maslahat: Bo'lak — komponentlar orasida. React bitta komponent funksiyasining o'rtasida to'xtay olmaydi:
Dishichidagi 50 ms li sikl oxirigacha ishlaydi. Shuning uchun og'ir ishni ko'p kichik komponentga bo'lish concurrent rendering'ga yordam beradi.
3.3 To'xtatilgan render — vaqt chizig'ida
O'sha tablar, lekin "Taomlar" transition bilan ochiladi (sxema, yaxlitlangan):
| Vaqt | React | Ekranda |
|---|---|---|
| 0 ms | "Taomlar" bosildi: shoshilinch render (isPending: true) |
"Xush kelibsiz" + "Yuklanmoqda…" |
| 1–100 ms | transition: Dish 1 va 2, har biridan keyin navbatni tekshiradi |
o'zgarmagan |
| 60 ms | "Aloqa" bosildi — bosish 2-karta tugashini kutadi | — |
| 100 ms | bosish bajariladi; "Taomlar" renderi tashlanadi | — |
| ~101 ms | "Aloqa" chizildi va commit | "Aloqa" |
Sinxron variantda "Aloqa" ~400 ms da chiqqan edi, bu yerda ~100 ms da. "Taomlar" hech qachon ekranga chiqmaydi — kerak emas edi. Xuddi shu ikki variant gantt diagrammasida:
gantt
dateFormat x
axisFormat %L ms
section Sinxron
Taomlar renderi :0, 400
Aloqa kutadi :crit, 100, 400
Aloqa :400, 405
section Transition
Taomlar 1-2 karta :0, 100
Aloqa kutadi :crit, 60, 100
Aloqa :100, 105Qizil chiziq — bosish javobsiz turgan vaqt. Pastki qatorda u ancha qisqa: 40 ms, ya'ni bitta Dish qolgan qismi. Diagramma sxema: Dish ≈ 50 ms bizning kompyuterdagi o'lchovdan, qolgan raqamlar yaxlitlangan.
Tekshirib ko'ring: Agar
Dish50 ms emas, 500 ms bo'lsa (bitta juda og'ir komponent), transition yordam beradimi?
Javob
Deyarli yo'q. React komponent o'rtasida to'xtay olmaydi, demak bosish shu 500 ms tugashini kutadi. Transition faqat ish ko'p komponentga bo'lingan bo'lsa yordam beradi. Bitta og'ir hisobni esa bo'lish, useMemo bilan saqlab qo'yish (memo, useMemo, useCallback) yoki Web Worker'ga chiqarish kerak (Og'ir ishni UI'dan ajratish).
4. useTransition
4.1 Tab almashtirish — transition bilan
useTransition — yangilanishni transition deb belgilaydigan hook. U ikki narsa qaytaradi:
isPending—true, agar transition boshlangan, lekin hali ekranga chiqmagan bo'lsa;startTransition(fn)—fnni darhol chaqiradi va uning ichidagi hammaset…larni transition deb belgilaydi.
O'sha misol, faqat App o'zgardi:
import { useEffect, useState, useTransition } from "react";
function slowWork() {
let x = 0;
for (let i = 0; i < 20_000_000; i++) x += i % 7;
return x;
}
function Dish({ n }) {
slowWork(); // ataylab sekin: 50 ms dan ortiq
return <li>Taom {n}</li>;
}
function MenuTab() {
useEffect(() => console.log("ekranga chiqdi: Taomlar"), []);
const items = [];
for (let i = 1; i <= 8; i++) items.push(<Dish key={i} n={i} />);
return <ul>{items}</ul>;
}
function ContactTab() {
useEffect(() => console.log("ekranga chiqdi: Aloqa"), []);
return <p>Tel: +998 90 000 00 00</p>;
}
function App() {
const [tab, setTab] = useState("bosh");
const [isPending, startTransition] = useTransition();
console.log("App:", tab, isPending);
function selectTab(next) {
startTransition(() => setTab(next)); // og'ir — transition
}
return (
<div>
<button onClick={() => selectTab("taomlar")}>Taomlar</button>
<button onClick={() => selectTab("aloqa")}>Aloqa</button>
{tab === "bosh" && <p>Xush kelibsiz</p>}
{tab === "taomlar" && <MenuTab />}
{tab === "aloqa" && <ContactTab />}
</div>
);
}Konsolda:
App: bosh false
App: bosh true
App: taomlar false
App: bosh true
App: aloqa false
ekranga chiqdi: AloqaEkranda:
TaomlarAloqa
Tel: +998 90 000 00 00Bu olti qator — darsning eng muhim joyi. Har birini o'qiymiz:
| Qator | Qaysi render | Ekranga chiqdimi |
|---|---|---|
App: bosh false |
birinchi chizish | ha |
App: bosh true |
"Taomlar" bosildi: shoshilinch — faqat isPending |
ha |
App: taomlar false |
transition renderi boshlandi | yo'q — tashlandi |
App: bosh true |
"Aloqa" bosildi: yana shoshilinch | ha |
App: aloqa false |
yangi transition | ha |
Uchinchi qatorga e'tibor bering: React App ni tab: "taomlar" bilan chaqirdi va kartalarni chiza boshladi. Lekin "ekranga chiqdi: Taomlar" konsolda yo'q. Render boshlangan, lekin commit'ga yetmagan — "Aloqa" bosilganda React uni tashlab yubordi.
Yana bir narsa: bitta bosish ikki render beradi. Birinchisi shoshilinch — u faqat isPending ni true qiladi, tab hali eski. Ikkinchisi — transition, unda tab yangi, isPending esa false. Ikkalasi ham snapshot: har render o'z qiymatlarini ko'radi.
4.2 isPending — "yuklanmoqda" ko'rsatkichi
Transition vaqtida ekranda eski tab turadi. Mehmon bosganini bilishi kerak — aks holda yana bosadi. isPending shu uchun:
import { useState, useTransition } from "react";
function slowWork() {
let x = 0;
for (let i = 0; i < 20_000_000; i++) x += i % 7;
return x;
}
function Dish({ n }) {
slowWork();
return <li>Taom {n}</li>;
}
function MenuTab() {
const items = [];
for (let i = 1; i <= 8; i++) items.push(<Dish key={i} n={i} />);
return <ul>{items}</ul>;
}
function App() {
const [tab, setTab] = useState("bosh");
const [isPending, startTransition] = useTransition();
return (
<div>
<button
onClick={() => startTransition(() => setTab("taomlar"))}
>
Taomlar
</button>
{isPending && <p>Yuklanmoqda…</p>}
{tab === "bosh" && <p>Xush kelibsiz</p>}
{tab === "taomlar" && <MenuTab />}
</div>
);
}Bosishdan bir lahza keyin (tekshiruvchi 60 ms kutadi) ekranda:
Taomlar
Yuklanmoqda…
Xush kelibsizBu — transition'ning o'rtasi. Shoshilinch render "Yuklanmoqda…" ni darhol chiqardi, eski "Xush kelibsiz" esa joyida. Kartalar fonda chizilmoqda. Tugagach, "Yuklanmoqda…" va "Xush kelibsiz" birga yo'qoladi, sakkiz karta chiqadi — bitta commit'da. «Ishga tushir» bilan o'zingiz ko'ring: sekin kompyuterda "Yuklanmoqda…" yaxshi ko'rinadi.
Qayerda ko'rsatish — dizayn qarori. Ko'pincha butun sahifani emas, faqat bosilgan tugmani xiralashtirish yetadi: style={{ opacity: isPending ? 0.6 : 1 }}. Kichik ko'rsatkich yaxshi — tez kompyuterda u bir lahza miltillab qoladi, xolos.
4.3 startTransition — hooksiz
isPending kerak bo'lmasa yoki kod komponentdan tashqarida bo'lsa (masalan, oddiy modul funksiyasi), react paketining o'zidan startTransition bor:
import { startTransition, useState } from "react";
function App() {
const [dish, setDish] = useState("Osh");
console.log("chizish:", dish);
return (
<button onClick={() => startTransition(() => setDish("Manti"))}>
Manti
</button>
);
}Konsolda:
chizish: Osh
chizish: MantiBu yerda bitta bosishga bitta render: isPending yo'q, demak shoshilinch renderga sabab ham yo'q. Qolgani bir xil — yangilanish transition, uni to'xtatish mumkin. useTransition qaytargan startTransition ham shunday, faqat isPending ni boshqaradi. Uning havolasi barqaror (har renderda o'sha funksiya), shuning uchun effekt massiviga qo'shish shart emas.
Tekshirib ko'ring: Mehmon "Saqlash" tugmasini bosdi. Tugma "Saqlanmoqda…" ga aylanishi kerak. Bu yangilanish shoshilinchmi yoki transition?
Javob
Shoshilinch. Bosishning natijasi — tugma holati — darhol ko'rinishi kerak. useTransition da aynan shunday: isPending: true shoshilinch renderda keladi, siz startTransition ichiga qo'ygan og'ir yangilanish esa — transition'da.
5. Qidiruv: maydon shoshilinch, ro'yxat — transition
5.1 Ikki state
Endi vazifalar dagi muammo — qidiruv. Bu yerda nozik joy bor. Maydondagi matn ham, ro'yxat ham bitta query dan chizilsa, uni transition qilib bo'lmaydi: maydon kechikadi. Yechim — ikki state: text — maydon uchun (shoshilinch), query — ro'yxat uchun (transition). Bitta onChange ikkalasini yangilaydi.
Choyxona menyusi, qidiruvga avval "cho", keyin "choy" — ikki harakat ketma-ket (tekshiruvchi orasida 60 ms kutadi, xuddi tez yozayotgan odamdek). Har taom kartasi yana o'sha slowWork() bilan sekin:
import { memo, useEffect, useState, useTransition } from "react";
const MENU = ["Osh", "Ko'k choy", "Lag'mon", "Qora choy", "Manti",
"Limonli choy", "Chuchvara", "Zanjabil choy", "Somsa"];
function slowWork() {
let x = 0;
for (let i = 0; i < 20_000_000; i++) x += i % 7;
return x;
}
function Dish({ name }) {
slowWork();
return <li>{name}</li>;
}
const DishList = memo(function DishList({ query }) {
console.log("DishList chizilmoqda:", query);
useEffect(() => console.log("ekranda:", query), [query]);
const found = MENU.filter((d) => d.toLowerCase().includes(query));
return <ul>{found.map((d) => <Dish key={d} name={d} />)}</ul>;
});
function App() {
const [text, setText] = useState("");
const [query, setQuery] = useState("");
const [, startTransition] = useTransition();
function handleChange(e) {
setText(e.target.value); // shoshilinch: maydon
startTransition(() => setQuery(e.target.value)); // ro'yxat
}
return (
<div>
<input
placeholder="Qidiruv"
value={text}
onChange={handleChange}
/>
<DishList query={query} />
</div>
);
}Konsolda:
DishList chizilmoqda:
ekranda:
DishList chizilmoqda: cho
DishList chizilmoqda: choy
ekranda: choyEkranda:
Ko'k choy
Qora choy
Limonli choy
Zanjabil choyRo'yxat ikki marta chizila boshladi ("cho" va "choy"), lekin ekranga faqat oxirgisi chiqdi. "cho" renderi keyingi harf kelganda tashlandi — keraksiz natijani chizib o'tirmaslik shu. Maydon esa har harfda darhol yangilandi: shoshilinch render DishList ga umuman tegmadi (konsolda bo'sh query bilan qayta chizish yo'q).
const [, startTransition] — massivdan birinchi elementni tashlab o'tish (Destructuring): bu misolda isPending kerak emas.
5.2 memo siz — transition yordam bermaydi
Oxirgi gap muhim: "shoshilinch render DishList ga tegmadi". Bunga memo sabab. memo — komponentni o'raydigan funksiya: props avvalgisi bilan bir xil bo'lsa (Object.is), React uni qayta chizmaydi, eski natijani oladi. To'liq — memo, useMemo, useCallback darsida, hozir shu bitta g'oya yetadi.
memo ni olib tashlaymiz — qolgani o'sha. Quyidagi blok oldingisining davomi: DishList ni oddiy funksiya bilan almashtiradi. Konsolda bo'sh query bilan chiqqan qatorlarni sanang:
function DishList({ query }) {
console.log("DishList chizilmoqda:", query);
useEffect(() => console.log("ekranda:", query), [query]);
const found = MENU.filter((d) => d.toLowerCase().includes(query));
return <ul>{found.map((d) => <Dish key={d} name={d} />)}</ul>;
}Konsolda:
DishList chizilmoqda:
ekranda:
DishList chizilmoqda:
DishList chizilmoqda: cho
DishList chizilmoqda:
DishList chizilmoqda: choy
ekranda: choyNima bo'ldi? Ikki qo'shimcha qator — bo'sh query bilan. Bu shoshilinch renderlar: setText App ni qayta chizdi, App esa sukut bo'yicha bolalarini ham chizadi (React ilovani debug qilish darsidagi "Soat" misoli). DishList eski query bilan — to'liq, sekin, to'xtatib bo'lmaydigan renderda! Maydon yana qotadi. Natija to'g'ri, ekran yakunda bir xil, lekin transition'dan foyda yo'q.
Diqqat: Transition og'ir qismni shoshilinch renderdan chiqarib tashlagandagina ishlaydi. Shoshilinch render og'ir komponentni baribir chizsa (props'i o'zgarmagan bo'lsa ham) — u yerda
memokerak.vazifalarda ham aynan shunday bo'ldi — «Vazifalar qadami» bo'limida.
5.3 Ikki state'ning narxi
Ishlaydi, lekin kuzating: endi qidiruvni o'zgartiradigan har joy ikki setter'ni chaqirishi kerak. vazifalar da setQidiruv uch joyda: maydon, Esc bilan tozalash va URL'dan tiklash. Bittasini unutsangiz, maydon va ro'yxat ajralib qoladi. Keyingi darsdagi useDeferredValue bu muammoni bitta state bilan hal qiladi.
6. Async transition (React 19)
6.1 Serverga saqlash
React 19 da startTransition ga async funksiya berish mumkin. Unda isPending funksiya tugaguncha — ya'ni await qilingan so'rov qaytguncha — true qoladi. React hujjati bunday funksiyani Action deb ataydi.
import { useState, useTransition } from "react";
// Soxta server: 300 ms dan keyin javob
function saveToServer(name) {
return new Promise((resolve) => {
setTimeout(() => resolve(`${name} saqlandi`), 300);
});
}
function App() {
const [message, setMessage] = useState("");
const [isPending, startTransition] = useTransition();
console.log("App:", isPending, message);
function save() {
startTransition(async () => {
const result = await saveToServer("Osh");
startTransition(() => setMessage(result)); // await dan keyin
});
}
return (
<div>
<button onClick={save} disabled={isPending}>Saqlash</button>
<p>{isPending ? "Saqlanmoqda…" : message}</p>
</div>
);
}Konsolda:
App: false
App: true
App: true
App: false Osh saqlandiEkranda:
Saqlash
Osh saqlandi| Render | isPending |
message |
Nima bo'ldi |
|---|---|---|---|
| 1 | false | "" | birinchi chizish |
| 2 | true | "" | bosish: shoshilinch, tugma o'chdi |
| 3 | true | "" | transition boshlandi, Action hali kutmoqda |
| 4 | false | "Osh saqlandi" | server javob berdi, Action tugadi |
Uchinchi render — React transition'ni darhol boshladi, lekin Action'ning await i hali tugamagan. Shuning uchun isPending hali true. Server javob bergach, setMessage va isPending: false bitta renderda keladi — ekranda "Saqlanmoqda…" va "Osh saqlandi" orasida bo'sh oraliq kadr yo'q.
Ilgari buni isSaving state'i, try/finally va ikki setter bilan qo'lda yozardik. Formalar II darsidagi <form action> va useActionState aynan shu mexanizm ustiga qurilgan — ular ichida transition bor. Action'lar, form va xatolar — Actions darsida.
6.2 Tuzoq: await dan keyingi setter
Diqqat qiling: setMessage yana bir startTransition ichida. Nega? React hujjati (react.dev, useTransition, 2026-10 holati) buni "ma'lum cheklov" deydi: await dan keyin chaqirilgan set… transition deb belgilanmaydi. Sababi — startTransition "hozir chaqirilayotgan setterlar"ni belgilaydi, await esa funksiyani to'xtatib, keyinroq boshqa vazifada davom ettiradi (async / await). U paytda React endi "transition ichidamiz" deb bilmaydi.
startTransition(async () => {
const result = await saveToServer("Osh");
setMessage(result); // ❌ transition emas — oddiy yangilanish
});Bu kod ham ishlaydi, lekin setMessage shoshilinch bo'lib qoladi. Og'ir ro'yxatni yangilasa — ekran qotadi, yangilanish to'xtatilmaydi. Qoida: await dan keyingi har setter — yana startTransition(() => …) ichida.
Tekshirib ko'ring: Konsolda nega aynan to'rt qator?
isPendingni qayerda o'zgartirdik?
Javob
Biz isPending ni hech qayerda o'zgartirmadik — uni React boshqaradi. Bosishda u shoshilinch renderda true bo'ldi (2-qator), Action tugaganda false bo'ldi (4-qator). 3-qator — transition renderi, u Action davomida bajarildi. message faqat 4-qatorda o'zgardi, chunki setMessage faqat server javobidan keyin chaqirildi.
7. Cheklovlar
React hujjati useTransition uchun bir nechta qoida beradi. Ular darsdagi tajribalardan kelib chiqadi:
| Qoida | Nega |
|---|---|
| Controlled input qiymati transition'da bo'lmasin | maydon kechikadi, kursor sakraydi — matn shoshilinch |
setTimeout ichidagi setter — transition emas |
fn darhol chaqiriladi, taymer keyin ishlaydi |
await dan keyingi setter — yana startTransition |
«Tuzoq» bo'limidagi sabab |
set funksiyasiga kirish kerak |
props'dan kelgan qiymat uchun — useDeferredValue |
| Bir nechta transition birga bajariladi | React ularni bitta renderga yig'adi (hozircha) |
Yana bitta ichki detal. Transition'ni React abadiy to'xtatib turmaydi. react-dom 19.3.0 kodida transition yangilanishining muddati — 5 soniya (currentTime + 5e3). Shundan ko'p kechiksa (masalan, foydalanuvchi to'xtovsiz yozib turibdi), React uni endi to'xtatmay oxirigacha chizadi. Aks holda ekran hech qachon yangilanmay qolardi.
8. Vazifalar qadami: useTransition varianti va o'lchov
8.1 Vazifa: 10 000 vazifada qidiruv
Yangi branch: perf/qidiruv-kechiktirish. Bu va keyingi dars — kanonda bitta qadam (holat 02). Kanonda useDeferredValue g'olib bo'ldi. Lekin u bilan solishtirish uchun avval useTransition varianti o'lchandi — biz ham shu tartibda boramiz. Commit — useDeferredValue darsi oxirida.
v7 da ro'yxat to'g'ridan-to'g'ri qidiruv dan hisoblanardi:
const korinadigan = korinadiganVazifalar(
vazifalar,
filtr,
saralash,
qidiruv,
);8.2 Variant: ikki state va transition
«Qidiruv: maydon shoshilinch, ro'yxat — transition» bo'limidagi g'oya, vazifalar da. Kanondagi variant fayli (natijalar/01-transition.diff — kanon bilan farqi):
- const [qidiruv, setQidiruv] = useState("");
+ const [qidiruv, setQidiruvMaydon] = useState("");
+ // 18/#01 VARIANT: ro'yxat so'rovi — alohida state,
+ // transition bilan
+ const [sorov, setSorov] = useState("");
+ const [kutilmoqda, boshla] = useTransition();
+ function setQidiruv(matn: string) {
+ setQidiruvMaydon(matn); // shoshilinch: maydon
+ boshla(() => setSorov(matn)); // transition: ro'yxat
+ }setQidiruv endi funksiya — uchta chaqiruv joyi (maydon, Esc, URL'dan tiklash) o'zgarmaydi, hammasi shu o'ram orqali o'tadi. Ro'yxat sorov dan hisoblanadi, maydon qidiruv ni ko'rsatadi. kutilmoqda (isPending) ro'yxatni xiralashtiradi. Nomlar o'zbekcha — kanon qoidasi (STANDART K4): boshla — startTransition. Birinchi izoh qatori telefonga sig'ishi uchun ikkiga bo'lindi.
Bu yetarli emas edi. «memo siz — transition yordam bermaydi» bo'limidagi muammo vazifalar da ham chiqdi: VazifaRoyxati memo bilan o'raldi, ro'yxat esa useMemo bilan saqlandi. Yana bir to'siq — har renderda yangi context qiymati. U memo ni ham aylanib o'tadi. Uchalasini keyingi darsda kanon kodi bilan qatorma-qator o'qiymiz.
8.3 O'lchov
O'lchov skripti (olchov/qidiruv-tezligi.mjs) 10 000 vazifani localStorage ga yozadi va qidiruvga haqiqiy klaviatura bilan "vazifa 12" ni yozadi — har harf 120 ms da, odam tezligida. Brauzerning o'zi har bosishni o'lchaydi (Event Timing API — INP shu yozuvlardan hisoblanadi, Core Web Vitals). Har variant 5 marta, mediana:
- v7: sinxronishlov 1024 ms1 184 ms
- useTransition (ikki state)ishlov 60 ms168 ms
- useDeferredValue (kanon)ishlov 62 ms208 ms
Manba: O'lchandi: vazifalar v7 va holat 02, olchov/qidiruv-tezligi.mjs, Chrome 154 headless, Intel Core i5-12500H, 5 marta mediana, 2026-10-13
| Eng uzun javob | Ishlovchi | ≥ 16 ms hodisalar | |
|---|---|---|---|
| v7 — sinxron | 1 184 ms | 1 024 ms | 38 ta |
useTransition varianti |
168 ms | 60 ms | 39 ta |
useDeferredValue — kanon |
208 ms | 62 ms | 40 ta |
Uchta xulosa:
- Ishlovchi 17 barobar qisqardi (1 024 → 60 ms). Harf endi transition renderini kutmaydi.
- Sekin hodisalar soni o'zgarmadi (38–40). Ish kamaymadi — React baribir 10 000 qatorni ko'rib chiqadi. Transition ishni kamaytirmaydi, uni to'xtatib bo'ladigan qiladi.
- Qolgan ~170 ms —
Appdagi boshqa hisoblar va katta ro'yxat commit'i. Ular memo va virtualizatsiya darslarida kamayadi.
Transition (168) va kanon (208) orasidagi farq — shovqin ichida: besh o'lchovda transition 152–248 ms, kanon 160–464 ms oraliqda yurdi. Kanon nega soddaroq yo'lni tanladi — keyingi darsda.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| maydon kechikadi, kursor sakraydi | input qiymati startTransition ichida |
maydon state'i — shoshilinch, ro'yxat — transition |
| transition bor, lekin ekran baribir qotadi | og'ir bola shoshilinch renderda ham chiziladi | memo va barqaror props |
await dan keyin ekran qotadi |
setter await dan keyin, transition'siz |
startTransition(() => set…) ichiga |
setTimeout ichidagi yangilanish to'xtatilmaydi |
taymer startTransition tugagach ishlaydi |
taymer ichida startTransition chaqiring |
| transition yordam bermadi | bitta komponent o'zi juda og'ir | ishni bo'lish, useMemo, Web Worker |
| "Yuklanmoqda…" tez kompyuterda miltillaydi | katta ko'rsatkich darhol chiqadi | kichik ko'rsatkich (xiralashtirish) |
useTransition ni komponentdan tashqarida chaqirish — hooklar qoidasi buzilishi. Komponentdan tashqarida react dagi startTransition ishlatiladi.
10. Mashqlar
Mashqlar kurs/mashqlar/18/01-usetransition/ da. Har yechimni «Ishga tushir» bilan sinang.
1-mashq (oson): shoshilinchmi yoki transition?
Har yangilanish uchun yozing: shoshilinch yoki transition.
- Qidiruv maydoniga harf yozildi — maydondagi matn:
- O'sha harf bo'yicha 5 000 taomli ro'yxat:
- "Buyurtmalar" tabi bosildi — tab mazmuni (og'ir jadval):
- Katak belgilandi — katakning o'zi:
Yechim
Foydalanuvchi o'z harakatining bevosita natijasini kutadigan joy — shoshilinch: yozilgan harf, belgilangan katak. Undan kelib chiqadigan og'ir ko'rinish — transition: ro'yxat, tab mazmuni. Bitta harakat ikkalasini birga keltirishi mumkin. Shunda bitta ishlovchida ikki setter bo'ladi, biri startTransition ichida — «Ikki state» bo'limidagidek.
2-mashq (o'rta): «Bahor» boshqaruv paneli
Jasur akaning panelida ikki tab: "Buyurtmalar" (sekin, 6 ta og'ir qator) va "Sozlamalar". Hozir tab oddiy setTab bilan almashadi. Uni shunday o'zgartiring:
- tab almashishi transition bo'lsin (
useTransition); - transition vaqtida tablar ostida
Yuklanmoqda…chiqsin; - "Buyurtmalar" chizilayotganda "Sozlamalar" bosilsa — "Buyurtmalar" umuman ekranga chiqmasin.
import { useEffect, useState } from "react";
function slowWork() {
let x = 0;
for (let i = 0; i < 20_000_000; i++) x += i % 7;
return x;
}
function OrderRow({ n }) {
slowWork();
return <li>Buyurtma №{n}</li>;
}
function OrdersTab() {
useEffect(() => console.log("ekranda: Buyurtmalar"), []);
const rows = [];
for (let i = 1; i <= 6; i++) rows.push(<OrderRow key={i} n={i} />);
return <ul>{rows}</ul>;
}
function SettingsTab() {
useEffect(() => console.log("ekranda: Sozlamalar"), []);
return <p>Ish vaqti: 08:00–22:00</p>;
}
function App() {
const [tab, setTab] = useState("sozlama");
return (
<div>
<button onClick={() => setTab("buyurtma")}>Buyurtmalar</button>
<button onClick={() => setTab("sozlama")}>Sozlamalar</button>
{tab === "buyurtma" ? <OrdersTab /> : <SettingsTab />}
</div>
);
}Konsolda:
ekranda: Sozlamalar
ekranda: Buyurtmalar
ekranda: SozlamalarYechim
import { useEffect, useState, useTransition } from "react";
function slowWork() {
let x = 0;
for (let i = 0; i < 20_000_000; i++) x += i % 7;
return x;
}
function OrderRow({ n }) {
slowWork();
return <li>Buyurtma №{n}</li>;
}
function OrdersTab() {
useEffect(() => console.log("ekranda: Buyurtmalar"), []);
const rows = [];
for (let i = 1; i <= 6; i++) rows.push(<OrderRow key={i} n={i} />);
return <ul>{rows}</ul>;
}
function SettingsTab() {
useEffect(() => console.log("ekranda: Sozlamalar"), []);
return <p>Ish vaqti: 08:00–22:00</p>;
}
function App() {
const [tab, setTab] = useState("sozlama");
const [isPending, startTransition] = useTransition();
function selectTab(next) {
startTransition(() => setTab(next));
}
return (
<div>
<button onClick={() => selectTab("buyurtma")}>
Buyurtmalar
</button>
<button onClick={() => selectTab("sozlama")}>Sozlamalar</button>
{isPending && <p>Yuklanmoqda…</p>}
{tab === "buyurtma" ? <OrdersTab /> : <SettingsTab />}
</div>
);
}Konsolda:
ekranda: SozlamalarEkranda:
BuyurtmalarSozlamalar
Ish vaqti: 08:00–22:00"ekranda: Buyurtmalar" yo'q — uning renderi "Sozlamalar" bosilganda tashlandi. "ekranda: Sozlamalar" ham faqat bir marta — birinchi chizishda. Ikkinchi bosish tab ni yana "sozlama" qildi. React buni o'sha qiymat deb ko'rdi va SettingsTab ni qayta yasamadi: u hech qachon ekrandan ketmagan edi.
3-mashq (qiyin): buyurtma yuborish — async transition
App da "Buyurtma berish" tugmasi bor. Soxta server sendOrder() 300 ms dan keyin buyurtma raqamini qaytaradi. Yozing:
- tugma bosilganda async transition boshlansin;
- kutish vaqtida tugma o'chiq (
disabled) va ostidaYuborilmoqda…; - javob kelgach:
Buyurtma №7 qabul qilindi.
Ishora: «Tuzoq: await dan keyingi setter» bo'limi.
function sendOrder() {
return new Promise((resolve) => setTimeout(() => resolve(7), 300));
}Yechim
import { useState, useTransition } from "react";
function sendOrder() {
return new Promise((resolve) => setTimeout(() => resolve(7), 300));
}
function App() {
const [orderId, setOrderId] = useState(null);
const [isPending, startTransition] = useTransition();
function submit() {
startTransition(async () => {
const id = await sendOrder();
startTransition(() => setOrderId(id)); // ✅ yana transition
});
}
return (
<div>
<button onClick={submit} disabled={isPending}>
Buyurtma berish
</button>
{isPending && <p>Yuborilmoqda…</p>}
{!isPending && orderId !== null && (
<p>Buyurtma №{orderId} qabul qilindi</p>
)}
</div>
);
}Ekranda:
Buyurtma berish
Buyurtma №7 qabul qilindiisSaving state'i ham, try/finally ham kerak bo'lmadi — isPending Action tugaguncha true. disabled={isPending} ikkinchi bosishni ham to'xtatadi: mehmon sabrsizlanib ikki marta bossa, ikki buyurtma ketmaydi. Xato bo'lsa nima bo'ladi (server rad etsa) — Actions va Error boundary darslarida.
4-mashq: vazifalar ga nazar
perf/qidiruv-kechiktirish branch'ida «Variant: ikki state va transition» bo'limidagi o'zgarishni kiriting. npm run build qiling va brauzerda DevTools → Console'da 10 000 vazifa yarating: localStorage.setItem("vazifalar:v3", …) (skript olchov/qidiruv-tezligi.mjs dagi paket ni namuna qiling). Qidiruvga tez yozing va v7 bilan farqni his qiling. DevTools → Performance yozuvida bitta harfning "Interaction" uzunligini ikkala variantda solishtiring. Commit qilmang — kanon varianti keyingi darsda.
Yechim
Kutiladigan natija: v7 da har harf yuzlab millisekund (bizning o'lchovda 1 024 ms ishlov), variantda — o'nlab (60 ms). Agar farq kichik bo'lsa — VazifaRoyxati memo siz yoki context qiymati har renderda yangi: shoshilinch render baribir 10 000 qatorni chizadi. Bu ikki tuzatish keyingi darsning «Vazifalar qadami» da. Performance yozuvida transition renderi bitta uzun blok emas, ko'p qisqa bo'laklar bo'lib ko'rinadi — bu concurrent rendering'ning o'zi.
11. Real ishda
- Tab va sahifa almashtirish: router kutubxonalari (React Router, Next.js) sahifa o'tishini sukut bo'yicha transition qiladi — yangi sahifa tayyorlanguncha eski sahifa interaktiv qoladi (SPA routing).
- Qidiruv va filtrlar: katta jadval, xarita, grafik — "maydon darhol, natija fonda" naqshi admin panellarda eng ko'p uchraydi.
- Formalar: React 19 dagi
<form action>,useActionStatevauseOptimistic— hammasi transition ustiga qurilgan (Actions). - Intervyu savollari: "Concurrent rendering nima?", "
useTransitionvauseDeferredValuefarqi?", "Transition ishni tezlashtiradimi?" (javob: yo'q — to'xtatib bo'ladigan qiladi), "Nega render toza bo'lishi kerak?".
Xulosa
- Sinxron render boshlansa, oxirigacha bajariladi — shu vaqtda bosish va yozish kutadi.
- Concurrent rendering: transition renderi ~5 ms li bo'laklarda, orada shoshilinch ish o'tkaziladi; keraksiz bo'lgan render tashlanadi, commit esa doim bir martada.
const [isPending, startTransition] = useTransition(): bosish ikki render beradi — shoshilinch (isPending: true) va transition.- Transition faqat og'ir qism shoshilinch renderdan chiqsa ishlaydi: ikki state va
memo. - React 19: async Action —
isPendingawaittugagunchatrue;awaitdan keyingi setter yanastartTransitionichida. vazifalar, 10 000 vazifa: eng uzun javob 1 184 → 168 ms, ishlovchi 1 024 → 60 ms; sekin hodisalar soni o'zgarmadi — ish kamaymaydi, to'xtatib bo'ladigan bo'ladi.
Keyingi dars: useDeferredValue — o'sha natija bitta state bilan: React qiymatning "kechiktirilgan nusxasi"ni o'zi yuritadi; vazifalar kanoni va commit.
Manbalar
- React:
useTransition— react.dev/reference/react/useTransition - React:
startTransition— react.dev/reference/react/startTransition - React 19 e'loni: "Actions" (2024-12-05) — react.dev/blog/2024/12/05/react-19
- React 18 e'loni: "What is Concurrent React?" (2022-03-29) — react.dev/blog/2022/03/29/react-v18
scheduler0.28.0 vareact-dom19.3.0 manba kodi (frameInterval = 5,computeExpirationTime) — npm paketlari- Event Timing API va INP — web.dev/articles/inp
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!