Mundarija (33)
- Bu darsda
- 1. Nega bu kerak?
- 2. Birinchi Suspense
- 2.1 Komponent "to'xtaydi"
- 2.2 To'xtash qanday ishlaydi
- 2.3 Chegara bo'lmasa
- 3. Nima suspend qiladi va nima qilmaydi
- 3.1 Uch manba
- 3.2 useEffect dagi fetch suspend qilmaydi
- 4. Ichma-ich chegaralar
- 4.1 Bitta chegara — hammasi birga
- 4.2 Ichki chegara — bo'lak-bo'lak
- 4.3 Chegarani qayerga qo'yish
- 5. Transition bilan fallback'ni yashirish
- 5.1 Muammo: tab almashganda miltillash
- 5.2 Yechim: startTransition
- 5.3 Chegara oldindan turishi kerak
- 6. Skeleton: fallback'ning shakli
- 7. Suspense kerak bo'lmagan joy: vazifalar ro'yxati
- 8. Vazifalar qadami: batafsil statistika (1-qism)
- 8.1 Nima quramiz
- 8.2 Sof hisob: batafsil-statistika.ts
- 8.3 Panel: StatistikaPaneli.tsx
- 8.4 Haqiqiy brauzerda nima bo'ldi
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): qaysi parda yopiq?
- 2-mashq (o'rta): menyu sharhlarni kutmasin
- 3-mashq (qiyin): tab almashganda eski kontent qolsin
- 4-mashq: Vazifalar qadami
- 11. Real ishda
- Xulosa
- Manbalar
Suspense: kutish holatini deklarativ ko'rsatish — fallback, ichma-ich chegaralar va transition
Qisqacha:
<Suspense fallback={…}>— "ichimdagi komponentlar tayyor bo'lmaguncha, ularning o'rnidafallbackni ko'rsat" degan chegara. Komponentuse(promise)yokilazybilan to'xtaydi (suspend), React esa eng yaqin chegarani topib, uning fallback'ini chizadi. Promise tayyor bo'lgach — haqiqiy kontent. "Yuklanmoqda…" endi har komponentdagiif (status === "loading")emas, daraxtdagi bitta joy. Chegara qayerda tursa, foydalanuvchi shuni ko'radi: bitta katta chegara — hammasi birga, ichma-ich chegaralar — bo'lak-bo'lak. Allaqachon ko'rinib turgan chegara ichidagi yangilanishnistartTransitionga o'rasangiz, React fallback o'rniga eski ekranni qoldiradi.
Bu darsda
<Suspense fallback>ni yozasiz va kutish holatini jonli ko'rasiz: avval fallback, keyin kontent.- Nima suspend qiladi (
use,lazy, framework ma'lumoti) va nima qilmaydi (useEffectdagifetch) — farqini bilasiz. - Ichma-ich chegaralar bilan sahifani bo'lak-bo'lak ochasiz va chegara joyini UX bo'yicha tanlaysiz.
- Transition bilan fallback "miltillashini" yo'qotasiz va nega chegara oldindan turishi kerakligini tushuntirasiz.
- Skeleton yasaysiz va
vazifalarda Suspense qayerda kerak, qayerda kerak emasligini o'lchov bilan asoslaysiz.
Oldin bilishingiz kerak: useTransition, useDeferredValue, Effektda ma'lumot olish, Promise asoslari.
1. Nega bu kerak?
Effektda ma'lumot olish darsida bitta taomni ko'rsatish uchun 30 qatorga yaqin kod yozdik: uchta state, effekt, abort va uchta return. Endi Jasur aka menyu sahifasini xohlaydi: tepada menyu, pastda mehmonlar sharhlari, yonida bugungi narxlar. Uchalasi ham serverdan keladi.
Sardor har komponentga o'z "Yuklanmoqda…" sini yozdi. Natija — "popkorn" sahifa: uchta yozuv har xil paytda paydo bo'lib, har xil paytda yo'qoladi. Sahifa sakraydi, ko'z qayerga qarashni bilmaydi. Jasur aka so'radi: "Menyu va narx birga chiqsin, sharhlar keyin kelsa ham mayli. Shuni qila olasizmi?" Har komponent o'z holatini o'zi boshqarsa — buni sozlash qiyin.
Hayotiy o'xshatish: teatr pardasi. Sahna tayyor bo'lmaguncha parda yopiq turadi — tomoshabin dekoratsiya ko'chirilayotganini ko'rmaydi. Aktyorlarning hammasi joyida bo'lgach, parda ochiladi. Katta spektaklda har sahna qismining o'z pardasi bo'lishi mumkin. Suspense — ana shu parda. Qayerga osilishini siz tanlaysiz, ochilish vaqtini React kuzatadi.
2. Birinchi Suspense
2.1 Komponent "to'xtaydi"
Komponentni kutishga majbur qilish uchun bizga use kerak. Uni Hooklar qoidalari va Context I darslarida Context'ni o'qish uchun ishlatgansiz. use Promise'ni ham qabul qiladi. Promise tayyor bo'lsa — uning qiymatini qaytaradi. Tayyor bo'lmasa — komponentni shu qatorda to'xtatadi. To'liq ichki ishlashi — keyingi darsda, bugun faqat shu qoida yetarli.
Promise'ni komponentdan tashqarida yaratamiz — modul yuklanganda bir marta. Nega bu muhim — keyingi darsning asosiy mavzusi. Hozircha fetch o'rnida 500 ms dan keyin menyu beradigan Promise:
import { Suspense, use, useEffect } from "react";
// fetch o'rnida: menyu 500 ms dan keyin keladi
const menuPromise = new Promise((resolve) => {
setTimeout(() => resolve(["Osh", "Lag'mon", "Manti"]), 500);
});
function Menu() {
console.log("Menu: chizishga urinish");
const dishes = use(menuPromise); // tayyor bo'lmasa — to'xtaydi
console.log("Menu: ma'lumot keldi");
return <p>{dishes.join(", ")}</p>;
}
function Loading() {
useEffect(() => console.log("fallback ekranga chiqdi"), []);
return <p>Menyu yuklanmoqda…</p>;
}
export default function App() {
return (
<section>
<h2>«Bahor» menyusi</h2>
<Suspense fallback={<Loading />}>
<Menu />
</Suspense>
</section>
);
}Konsolda (100 ms da):
Menu: chizishga urinish
fallback ekranga chiqdi
Menu: chizishga urinishEkranda (100 ms da):
«Bahor» menyusi
Menyu yuklanmoqda…Endi o'sha kodni o'zgartirmasdan 700 ms kutamiz:
// yuqoridagi kod — o'zgarishsiz, faqat kutish vaqti boshqaKonsolda (700 ms da):
Menu: chizishga urinish
fallback ekranga chiqdi
Menu: chizishga urinish
Menu: chizishga urinish
Menu: ma'lumot keldiEkranda (700 ms da):
«Bahor» menyusi
Osh, Lag'mon, Manti«Ishga tushir» ni bosing: yarim soniya "Menyu yuklanmoqda…", keyin menyu. Konsolga sekin qarang:
Menuchizila boshladi,useqatorida to'xtadi. Ikkinchiconsole.loggacha yetib bormadi.- React eng yaqin
<Suspense>ni topdi vafallbackni ekranga chiqardi.Loadingeffekti buni tasdiqladi. - Yana bir
Menu: chizishga urinish. Bu React 19 ning prewarm ("oldindan isitish") odati. Fallback ko'ringach, React to'xtagan qismni fonda yana bir bor chizib ko'radi. Maqsad — daraxtning chuqurroq joyidagi boshqa so'rov valazykodlar ham erta boshlansin. - 500 ms da Promise hal bo'ldi. React
Menuni qaytadan chizdi — bu safaruseqiymat qaytardi.
Menu ning ichida isLoading ham, useEffect ham, useState ham yo'q. U faqat tayyor ma'lumotni chizadi. Kutish holati butunlay yuqoriga — chegaraga ko'chdi.
| Vaqt | Menu |
Ekranda |
|---|---|---|
| 0 ms | use da to'xtadi |
fallback: "Menyu yuklanmoqda…" |
| 0 ms dan keyin | prewarm: yana to'xtadi | fallback |
| 500 ms | Promise hal bo'ldi, qayta chizildi | "Osh, Lag'mon, Manti" |
2.2 To'xtash qanday ishlaydi
Suspend (to'xtash) — komponent render paytida "men hali tayyor emasman" deb React'ga bildiradi. React bu komponentni chala holda ekranga qo'ymaydi. U daraxt bo'ylab yuqoriga chiqib, eng yaqin <Suspense> ni qidiradi. Chegara (boundary) — shu <Suspense>: uning ichidagi hamma narsa o'rniga fallback chiziladi.
Diagrammada «ha» yo'li — oddiy render, «yo'q» yo'li — to'xtash. Halqaga qarang: Promise hal bo'lgach, React o'sha komponentni boshidan qayta chizadi.
flowchart TD
A["React Menu ni chizadi"] --> B{"use: Promise tayyormi?"}
B -- "ha" --> C["Qiymat qaytadi, kontent chiziladi"]
B -- "yo'q" --> D["Menu to'xtaydi (suspend)"]
D --> E["Eng yaqin Suspense topiladi"]
E --> F["fallback ekranga chiqadi"]
F --> G["Promise hal bo'ladi"]
G --> AE'tibor bering: React to'xtagan komponentning davomidan emas, boshidan chizadi. Shuning uchun komponent pure bo'lishi muhim. Uni necha marta chaqirsangiz ham, bir xil natija berishi kerak.
2.3 Chegara bo'lmasa
<Suspense> ni olib tashlasak-chi? To'xtagan komponent baribir eng yaqin chegarani qidiradi. Topolmasa — butun ildiz (root) chegara vazifasini bajaradi:
import { use } from "react";
const dishPromise = new Promise((resolve) => {
setTimeout(() => resolve("Osh"), 300);
});
function Menu() {
return <p>{use(dishPromise)}</p>;
}
export default function App() {
return (
<div>
<h2>Menyu</h2>
<Menu />
</div>
);
}Ekranda (100 ms da):
(bo'sh)Sarlavha "Menyu" ham yo'q, garchi uning kutishga hech qanday aloqasi bo'lmasa ham. 300 ms dan keyin ikkalasi birdan paydo bo'ladi. Haqiqiy ilovada bu — oq ekran. Xulosa: use yoki lazy ishlatsangiz, ularning tepasida doim <Suspense> bo'lsin.
Tekshirib ko'ring: Birinchi misolda
Menunecha marta chaqirildi va negaMenu: ma'lumot keldifaqat bir marta chiqdi?
Javob
Uch marta: birinchi urinish, prewarm va Promise hal bo'lgandan keyingi chizish. Dastlabki ikkitasida komponent use qatorida to'xtadi — undan keyingi kod bajarilmadi. Faqat uchinchisida use qiymat qaytardi va funksiya oxirigacha ishladi. Shuning uchun render ichida yon ta'sir (so'rov yuborish, hisoblagichni oshirish) qilish xavfli — u kutilganidan ko'p marta bajariladi.
3. Nima suspend qiladi va nima qilmaydi
3.1 Uch manba
Suspense'ni faqat maxsus mexanizmlar "uyg'otadi":
| Manba | Nima kutiladi | Qayerda o'rganamiz |
|---|---|---|
use(promise) |
Promise'ning qiymati | bugun va use() |
lazy(() => import(…)) |
komponent kodi (alohida fayl) | lazy |
| framework ma'lumoti | Next.js server komponentlari, TanStack Query useSuspenseQuery |
Streaming va Suspense, TanStack Query |
lazy — komponent kodini kerak bo'lgandagina yuklaydi. Kod kelguncha komponent to'xtaydi. Sayt oynasida alohida fayl import qilib bo'lmaydi. Shuning uchun import("./Reviews.jsx") o'rnida o'sha shakldagi Promise beramiz: { default: Komponent }.
import { lazy, Suspense, useState } from "react";
// import("./Reviews.jsx") o'rnida: 300 ms dan keyin modul keladi
const Reviews = lazy(() => {
console.log("Reviews kodi yuklanmoqda");
return new Promise((resolve) => {
setTimeout(() => resolve({ default: ReviewsList }), 300);
});
});
function ReviewsList() {
return <p>Sharhlar: 2 ta</p>;
}
export default function App() {
const [open, setOpen] = useState(false);
return (
<div>
<button onClick={() => setOpen(true)}>Sharhlarni ochish</button>
<Suspense fallback={<p>Kod yuklanmoqda…</p>}>
{open && <Reviews />}
</Suspense>
</div>
);
}Konsolda:
Reviews kodi yuklanmoqdaEkranda (bosgandan 100 ms keyin):
Sharhlarni ochish
Kod yuklanmoqda…Tugma bosilmaguncha sharhlar kodi so'ralmaydi ham. Bosilgach — kod keladi, shu paytgacha fallback turadi. Mexanizm use bilan bir xil, faqat kutilgan narsa — ma'lumot emas, kod.
3.2 useEffect dagi fetch suspend qilmaydi
Sardor eski Menu ni (effekt bilan) <Suspense> ichiga qo'ydi va fallback chiqishini kutdi:
import { Suspense, useEffect, useState } from "react";
function Menu() {
const [dishes, setDishes] = useState(null);
useEffect(() => {
const id = setTimeout(() => setDishes(["Osh", "Manti"]), 300);
return () => clearTimeout(id);
}, []);
if (dishes === null) return <p>Menu o'zi: yuklanmoqda…</p>;
return <p>{dishes.join(", ")}</p>;
}
function Loading() {
useEffect(() => console.log("fallback ekranga chiqdi"), []);
return <p>Suspense: yuklanmoqda…</p>;
}
export default function App() {
return (
<Suspense fallback={<Loading />}>
<Menu />
</Suspense>
);
}Ekranda (100 ms da):
Menu o'zi: yuklanmoqda…Konsol bo'sh — fallback bir marta ham chiqmadi. Menu to'xtamadi: u darhol <p> qaytardi va React buni "tayyor" deb hisobladi. Effekt chizishdan keyin ishlaydi (useEffect I), Suspense esa render paytidagi to'xtashni ushlaydi. Ular bir-birini ko'rmaydi.
Diqqat: Suspense — "sehrli" kutish emas. U faqat
use,lazyva Suspense'ni qo'llaydigan kutubxonalar bilan ishlaydi.useEffect+useStatebilan yozilgan yuklanish o'zifiga tayanishda davom etadi.
Tekshirib ko'ring:
lazymisolida<Suspense>ni olib tashlasak, tugma bosilganda nima bo'ladi?
Javob
To'xtagan Reviews eng yaqin chegarani topolmaydi — ildizgacha chiqadi. Ildiz chegara vazifasini bajaradi, ya'ni kod kelguncha butun ilova ekrandan yo'qolishi mumkin, hatto tugma ham. «Chegara bo'lmasa» bo'limidagi oq ekranning o'zi.
4. Ichma-ich chegaralar
4.1 Bitta chegara — hammasi birga
Bitta <Suspense> ichida ikki komponent bo'lsa, React ikkalasini kutadi. Menyu 200 ms da tayyor, narxlar — 400 ms da:
import { Suspense, use } from "react";
function wait(ms, value) {
return new Promise((resolve) => {
setTimeout(() => resolve(value), ms);
});
}
const menuPromise = wait(200, "Osh, Lag'mon, Manti");
const pricePromise = wait(400, "Osh — 35000 so'm");
function Menu() {
return <p>{use(menuPromise)}</p>;
}
function Prices() {
return <p>{use(pricePromise)}</p>;
}
export default function App() {
return (
<Suspense fallback={<p>Yuklanmoqda…</p>}>
<Menu />
<Prices />
</Suspense>
);
}Ekranda (300 ms da):
Yuklanmoqda…Menyu 100 ms dan beri tayyor, lekin ko'rinmaydi. Parda bitta — sahnadagi eng sekin aktyor ham joyiga kelishi kerak. 400 ms da ikkalasi birga ochiladi. Jasur akaning birinchi talabi ("menyu va narx birga chiqsin") aynan shu.
4.2 Ichki chegara — bo'lak-bo'lak
Endi sharhlar sekin (800 ms). Ular menyuni ushlab turmasin — o'z pardasini beramiz:
import { Suspense, use } from "react";
function wait(ms, value) {
return new Promise((resolve) => {
setTimeout(() => resolve(value), ms);
});
}
const menuPromise = wait(300, ["Osh", "Lag'mon", "Manti"]);
const reviewsPromise = wait(800, ["Zo'r!", "Osh juda mazali"]);
function Menu() {
const dishes = use(menuPromise);
console.log("menyu tayyor");
return <p>Menyu: {dishes.join(", ")}</p>;
}
function Reviews() {
const reviews = use(reviewsPromise);
console.log("sharhlar tayyor");
return <p>Sharhlar: {reviews.length} ta</p>;
}
export default function App() {
return (
<Suspense fallback={<p>Sahifa yuklanmoqda…</p>}>
<Menu />
<Suspense fallback={<p>Sharhlar yuklanmoqda…</p>}>
<Reviews />
</Suspense>
</Suspense>
);
}Konsolda:
menyu tayyorEkranda (500 ms da):
Menyu: Osh, Lag'mon, Manti
Sharhlar yuklanmoqda…Uch bosqich bo'ldi. 0–300 ms: faqat tashqi fallback, "Sahifa yuklanmoqda…". Ichki fallback hali ko'rinmaydi — u tashqi pardaning orqasida. 300 ms: menyu chiqdi, sharhlar o'rnida ichki fallback. 800 ms: sharhlar ham chiqdi. Har use faqat o'ziga eng yaqin chegarani to'xtatadi: Reviews ning kutishi tashqi chegaraga tegmaydi.
flowchart TD
App --> S1["Suspense: Sahifa yuklanmoqda"]
S1 --> Menu["Menu (300 ms)"]
S1 --> S2["Suspense: Sharhlar yuklanmoqda"]
S2 --> Reviews["Reviews (800 ms)"]Daraxtga qarang: Menu ning eng yaqin chegarasi — tashqi, Reviews niki — ichki. Kim to'xtasa, faqat o'z chegarasini yopadi.
4.3 Chegarani qayerga qo'yish
Bu — texnik emas, dizayn qarori. Foydalanuvchi nimani birga ko'rishi kerak?
| Joylashuv | Ekranda | Qachon yaxshi |
|---|---|---|
| butun sahifaga bitta | hammasi birga chiqadi | qismlar bir-birisiz ma'nosiz (menyu + narx) |
| har komponentga alohida | "popkorn": har biri o'z vaqtida | deyarli hech qachon — sahifa sakraydi |
| ma'no bo'yicha guruh | asosiy qism tez, ikkinchi darajali keyin | ko'p holatda — menyu, keyin sharhlar |
Qoida: chegara — foydalanuvchi uchun bir butun bo'lgan qismning chetida. Muhim kontent sekin ikkinchi darajali kontentni kutib qolmasin. Mayda bo'laklarga bo'lish ham yomon: o'nta fallback birin-ketin almashsa, ko'z charchaydi.
Tekshirib ko'ring: Ichma-ich misolda menyu 900 ms da, sharhlar 200 ms da tayyor bo'lsa, ekranda nima bo'ladi?
Javob
900 ms gacha faqat "Sahifa yuklanmoqda…". Sharhlar tayyor, lekin ular tashqi chegaraning ichida — tashqi parda yopiq turibdi. 900 ms da menyu va sharhlar birga chiqadi. Ichki chegara faqat tashqi ochilgandan keyin foyda beradi.
5. Transition bilan fallback'ni yashirish
5.1 Muammo: tab almashganda miltillash
Menyu sahifasida ikki tab bor: «Menyu» va «Sharhlar». Har tab ma'lumoti birinchi ochilganda yuklanadi. fetchTab uni Map da saqlaydi — ikkinchi marta so'ramaydi. Bu oddiy kesh, uning nozik tomonlari — keyingi darsda. Fallback ekranga necha marta chiqqanini Loading effekti sanaydi:
import { Suspense, use, useEffect, useState } from "react";
const DATA = {
menyu: "Osh, Lag'mon, Manti",
sharhlar: "Zo'r! Osh juda mazali",
};
const cache = new Map();
function fetchTab(tab) {
if (!cache.has(tab)) {
console.log("so'rov:", tab);
const promise = new Promise((resolve) => {
setTimeout(() => resolve(DATA[tab]), 400);
});
cache.set(tab, promise);
}
return cache.get(tab);
}
function TabContent({ tab }) {
return <p>{use(fetchTab(tab))}</p>;
}
function Loading() {
useEffect(() => console.log("fallback ekranga chiqdi"), []);
return <p>Yuklanmoqda…</p>;
}
export default function App() {
const [tab, setTab] = useState("menyu");
return (
<div>
<button onClick={() => setTab("menyu")}>Menyu</button>{" "}
<button onClick={() => setTab("sharhlar")}>Sharhlar</button>
<Suspense fallback={<Loading />}>
<TabContent tab={tab} />
</Suspense>
</div>
);
}Konsolda:
so'rov: menyu
fallback ekranga chiqdi
so'rov: sharhlar
fallback ekranga chiqdiIkkinchi fallback ekranga chiqdi — muammo. Mehmon menyuni o'qib turgan edi. «Sharhlar» ni bosdi — menyu yo'qolib, "Yuklanmoqda…" chiqdi, 400 ms dan keyin sharhlar. Ko'rinib turgan kontent bir lahzaga bo'sh yozuvga almashdi. Bu yomon tajriba: ekran sakraydi, foydalanuvchi "nima bo'ldi?" deb o'ylaydi.
Qiziq detal: eski menyu DOM'dan o'chmaydi. React uni display: none bilan yashiradi va ichidagi state saqlanadi. Kontent qaytsa — yashirish olinadi.
5.2 Yechim: startTransition
useTransition darsidan bilasiz: transition — "bu yangilanish shoshilinch emas". Suspense bilan birga u yana bir ma'no oladi: "tayyor bo'lmaguncha eski ekranni qoldir". Faqat App o'zgaradi:
import {
Suspense, use, useEffect, useState, useTransition,
} from "react";
const DATA = {
menyu: "Osh, Lag'mon, Manti",
sharhlar: "Zo'r! Osh juda mazali",
};
const cache = new Map();
function fetchTab(tab) {
if (!cache.has(tab)) {
console.log("so'rov:", tab);
const promise = new Promise((resolve) => {
setTimeout(() => resolve(DATA[tab]), 400);
});
cache.set(tab, promise);
}
return cache.get(tab);
}
function TabContent({ tab }) {
return <p>{use(fetchTab(tab))}</p>;
}
function Loading() {
useEffect(() => console.log("fallback ekranga chiqdi"), []);
return <p>Yuklanmoqda…</p>;
}
export default function App() {
const [tab, setTab] = useState("menyu");
const [isPending, startTransition] = useTransition();
function selectTab(next) {
startTransition(() => setTab(next));
}
return (
<div>
<button onClick={() => selectTab("menyu")}>Menyu</button>{" "}
<button onClick={() => selectTab("sharhlar")}>Sharhlar</button>
<p>{isPending ? "Kuting…" : ""}</p>
<Suspense fallback={<Loading />}>
<TabContent tab={tab} />
</Suspense>
</div>
);
}Konsolda:
so'rov: menyu
fallback ekranga chiqdi
so'rov: sharhlarEkranda (bosgandan 100 ms keyin):
Menyu Sharhlar
Kuting…
Osh, Lag'mon, MantiFallback faqat bir marta — sahifa ochilganda chiqdi. «Sharhlar» bosilgach, menyu joyida qoldi, tepada "Kuting…". 400 ms dan keyin sharhlar birdan chiqadi. React yangi ekranni fonda tayyorlaydi va tayyor bo'lmaguncha commit qilmaydi: eski ekran rost, yangisi hali chala.
| Qadam | tab |
isPending |
Ekranda |
|---|---|---|---|
| ochilish | menyu | false |
fallback, keyin menyu |
| «Sharhlar» bosildi | fonda: sharhlar | true |
menyu + "Kuting…" |
| 400 ms | sharhlar | false |
sharhlar |
5.3 Chegara oldindan turishi kerak
Transition hamma joyda ham fallback'ni yashirmaydi. Ikki kodni solishtiring — farq faqat <Suspense> qayerdaligida.
Chegara shart ichida — tugma bosilganda yangi paydo bo'ladi:
import { Suspense, use, useState, useTransition } from "react";
const statsPromise = new Promise((resolve) => {
setTimeout(() => resolve("3 ta vazifa, 1 tasi bajarilgan"), 300);
});
function Stats() {
return <p>{use(statsPromise)}</p>;
}
export default function App() {
const [open, setOpen] = useState(false);
const [isPending, startTransition] = useTransition();
return (
<div>
<button onClick={() => startTransition(() => setOpen(true))}>
Batafsil
</button>
<p>{isPending ? "Hisoblanmoqda…" : ""}</p>
{open && (
<Suspense fallback={<p>Tayyorlanmoqda…</p>}>
<Stats />
</Suspense>
)}
</div>
);
}Ekranda (bosgandan 100 ms keyin):
Batafsil
Tayyorlanmoqda…Chegara tashqarida — sahifa ochilganidan beri bor, faqat ichi bo'sh edi:
import { Suspense, use, useState, useTransition } from "react";
const statsPromise = new Promise((resolve) => {
setTimeout(() => resolve("3 ta vazifa, 1 tasi bajarilgan"), 300);
});
function Stats() {
return <p>{use(statsPromise)}</p>;
}
export default function App() {
const [open, setOpen] = useState(false);
const [isPending, startTransition] = useTransition();
return (
<div>
<button onClick={() => startTransition(() => setOpen(true))}>
Batafsil
</button>
<p>{isPending ? "Hisoblanmoqda…" : ""}</p>
<Suspense fallback={<p>Tayyorlanmoqda…</p>}>
{open && <Stats />}
</Suspense>
</div>
);
}Ekranda (bosgandan 100 ms keyin):
Batafsil
Hisoblanmoqda…Ikkalasida ham startTransition bor, natija esa har xil. Qoida: transition allaqachon ekranda turgan chegarani fallback'ga qaytarmaydi — u yerdagi kontentni foydalanuvchi ko'rib turibdi. Yangi paydo bo'layotgan chegara esa hech narsani yashirmaydi, shuning uchun React uning fallback'ini darhol ko'rsatadi. Panel yoki tab'ni transition bilan ochmoqchi bo'lsangiz — <Suspense> ni shartdan tashqariga qo'ying. Bu qoidaga vazifalar da yana duch kelamiz.
Tekshirib ko'ring: Tab misolida (transition bilan) foydalanuvchi «Sharhlar» ni bosib, 100 ms o'tmay «Menyu» ni bossa, nima bo'ladi?
Javob
Menyu ma'lumoti keshda tayyor — fetchTab("menyu") o'sha hal bo'lgan Promise'ni qaytaradi. Yangi transition kutilayotgan «Sharhlar» ni bosib o'tadi: ekranda menyu qoladi, "Kuting…" yo'qoladi. Sharhlar so'rovi baribir tugaydi va keshga tushadi — keyingi bosishda ular darhol chiqadi.
6. Skeleton: fallback'ning shakli
Fallback faqat matn bo'lishi shart emas. Skeleton — kelajakdagi kontent shaklidagi kulrang bo'laklar: ro'yxat bo'lsa — uchta chiziq, karta bo'lsa — to'rtburchak. Foydalanuvchi nima kelishini oldindan ko'radi. Eng muhimi, kontent kelganda sahifa sakramaydi. Bu Web Vitals dagi CLS (layout siljishi) uchun foydali.
import { Suspense, use } from "react";
const menuPromise = new Promise((resolve) => {
setTimeout(() => resolve(["Osh", "Lag'mon", "Manti"]), 800);
});
function Menu() {
const dishes = use(menuPromise);
return (
<ul>
{dishes.map((d) => (
<li key={d}>{d}</li>
))}
</ul>
);
}
const bar = {
height: "1.2em",
width: "8em",
margin: "0.4em 0",
background: "#d4d4d8",
borderRadius: 4,
listStyle: "none",
};
function MenuSkeleton() {
return (
<ul aria-busy="true" aria-label="Menyu yuklanmoqda">
<li style={bar} />
<li style={bar} />
<li style={bar} />
</ul>
);
}
export default function App() {
return (
<section>
<h2>«Bahor» menyusi</h2>
<Suspense fallback={<MenuSkeleton />}>
<Menu />
</Suspense>
</section>
);
}Ekranda (100 ms da):
«Bahor» menyusiMatnda faqat sarlavha — skeleton'da yozuv yo'q. «Ishga tushir» natija oynasida esa uchta kulrang chiziq ko'rinadi, 800 ms dan keyin ular o'rnida uchta taom. Malika (ekran o'quvchi) uchun ro'yxatda aria-label="Menyu yuklanmoqda" va aria-busy="true" bor. Kulrang chiziqlarni u ko'rmaydi, lekin nima bo'layotganini eshitadi.
Skeleton bo'yicha uch maslahat:
- O'lchami haqiqiy kontentga yaqin bo'lsin — aks holda sakrash baribir bo'ladi.
- Juda qisqa kutishga (100 ms dan kam) skeleton kerak emas: u miltillab ketadi. React fallback'ni ko'rsatgach, uni kamida ~300 ms ushlab turadi — aynan miltillamasligi uchun.
- Butun sahifani skeleton bilan to'ldirmang. Faqat haqiqatan kutiladigan joyga qo'ying.
7. Suspense kerak bo'lmagan joy: vazifalar ro'yxati
Suspense'ni o'rgangach, uni hamma joyga qo'ygingiz keladi. vazifalar da birinchi nomzod — server ro'yxati. Ilova ochilganda u serverdan GET qiladi (useEffect III). Uni use(promise) va <Suspense> bilan yozsa bo'lmaydimi? Kanonni qurishda buni o'lchadik.
Birinchi ochilish — 5 ta toza Chrome 155 profili (localStorage bo'sh, kalit yangi). Natija (kanon o'lchovi, aynan):
- Ro'yxat ekranda sahifa yuklanishidayoq (
load, 140–181 ms) bor —localStoragedan yoki namunadan. - Server 5 martadan 5 marta
{"vazifalar":[],"versiya":0}qaytardi. Kalit yangi — server undan bexabar. Keyin ilova o'z ro'yxatini PUT bilan yuboradi.
GET qancha kutildi:
- 1-ochilish39 ms
- 2-ochilish61 ms
- 3-ochilish160 ms
- 4-ochilish42 ms
- 5-ochilish112 ms
Manba: O'lchandi: vazifalar v8 (holat 04), 5 ta toza Chrome 155.0.8059.40 profili, kurs serveri, 2026-10-13
Agar ro'yxat <Suspense> ichida serverni kutganda, mehmon har ochilishda 40–160 ms fallback ko'rardi. Server ishlamasa — so'rovning chegarasi 8 soniya, shuncha vaqt "Yuklanmoqda…". Va bu kutishning natijasi — bo'sh ro'yxat. Haqiqiy ma'lumot allaqachon ekranda edi.
Shuning uchun kanonda ro'yxat Suspense'siz qoldi: avval keshdagisi, server esa fonda solishtiriladi. Effektda ma'lumot olish darsidagi "stale-while-revalidate" ning o'zi. Suspense ma'lumot hali yo'q joyda foydali. Ma'lumot bor joyda u faqat to'sqinlik qiladi.
Maslahat: "Yangi vosita qo'shaymi?" degan savolga eng yaxshi javob — o'lchov. Biz shunday qildik: variant yozildi, o'lchandi va raqam bilan rad etildi. Real loyihada ham "Suspense zamonaviy" deganga emas, foydalanuvchi nima ko'rishiga qarang.
8. Vazifalar qadami: batafsil statistika (1-qism)
8.1 Nima quramiz
vazifalar dagi oddiy statistika ("2 ta vazifa qoldi") yonida yangi «Batafsil statistika» paneli. Unda teglar jadvali (jami va bajarilgan), eng ko'p uchragan so'zlar, foiz va o'rtacha matn uzunligi bor. Ro'yxat katta bo'lsa (10 000 vazifa), bu hisob sezilarli vaqt oladi. Shuning uchun panel natijani kutib oladi — Suspense va use bilan.
Bu qadam ikki darsga bo'lingan. Bugun — hisob, panel va chegara. Keyingi darsda — Promise keshi, test va commit. Hozirgi holatingiz — useDeferredValue darsidagi qidiruv tugagan joy.
git switch -c feat/batafsil-statistika8.2 Sof hisob: batafsil-statistika.ts
Hisob — React'siz sof funksiya (Pure komponentlar g'oyasi, faqat oddiy funksiya uchun). Kirish — vazifalar, chiqish — obyekt (kanon, turi va imzosi):
export type BatafsilStatistika = {
readonly jami: number;
readonly bajarildi: number;
/** 0..100, butun son */
readonly foiz: number;
/** matn uzunligining o'rtachasi (belgi), butun */
readonly ortachaUzunlik: number;
/** ko'p ishlatilgani oldin, teng bo'lsa — kod tartibida */
readonly teglar: readonly TegHisobi[];
/** eng ko'p uchragan so'zlar (teglarsiz, 3+ harf) */
readonly sozlar: readonly SozHisobi[];
};
export function batafsilHisobla(
vazifalar: readonly VazifaMalumoti[],
): BatafsilStatistika {Teglarni ajratish royxat.ts da allaqachon bor — faqat eksport qilamiz:
-const TEG =
+export const TEG =
/(?<![\p{L}\p{N}_#])#[\p{L}\p{N}_]+(?:['‘’][\p{L}\p{N}_]+)*/gu;
...
-function teglariniOl(matn: string): Set<string> {
+export function teglariniOl(matn: string): Set<string> {Testi — tekshiruv/batafsil-statistika.test.js, to'rtta node:test testi: bo'sh ro'yxat (nolga bo'lish yo'q), foiz va teglar, so'zlar (teglarsiz, apostrof va katta harf normallangan), eng ko'pi 10 ta so'z. Hisob tez va brauzersiz tekshiriladi. Panel esa unga faqat "tayyor natijani ko'rsat" deydi.
8.3 Panel: StatistikaPaneli.tsx
Asosiy fayl. Kod kanondan aynan, izohlar telefonga sig'ishi uchun qisqa qatorlarga bo'lingan:
// StatistikaPaneli.tsx — «Batafsil statistika» (ochiladigan
// panel, 18/#01, #03–#04). Uch g'oya bir joyda:
// - useTransition (#01): ochish — transition. Hisob tayyor
// bo'lmaguncha React ESKI ekranni (yopiq panel) qoldiradi,
// tugma yonida "Hisoblanmoqda…" (isPending); keyin panel
// birdan to'liq ochiladi
// - Suspense (#03): chegara panel ochilmasdan OLDIN bor
// (allaqachon ko'rsatilgan) — transition paytida fallback'ga
// almashmaydi. Fallback faqat transition'siz to'xtashda
// (masalan, birinchi ochilish transition'siz bo'lsa) ko'rinadi
// - use() (#04): BatafsilStatistika — keshlangan Promise'ni
// o'qiydi
// useDeferredValue: ro'yxat o'zgarsa (katak bosildi), eski
// statistika ko'rinib turadi, yangisi fonda hisoblanadi —
// fallback miltillamaydi
import {
Suspense,
useDeferredValue,
useId,
useState,
useTransition,
} from "react";
import { BatafsilStatistika } from "./BatafsilStatistika.tsx";
import type { VazifaMalumoti } from "../turlar.ts";
type Props = {
vazifalar: readonly VazifaMalumoti[];
};
export function StatistikaPaneli({ vazifalar }: Props) {
const [ochiq, setOchiq] = useState(false);
const [kutilmoqda, boshla] = useTransition();
const kechikkan = useDeferredValue(vazifalar);
const panelId = useId();
const sarlavhaId = useId();
const eskirgan = ochiq && kechikkan !== vazifalar;
return (
<section id="batafsil" aria-labelledby={sarlavhaId}>
{/* Disclosure naqshi (WAI-ARIA APG): sarlavha ichida tugma,
holat — aria-expanded */}
<h2 id={sarlavhaId}>
<button
type="button"
id="statistika-tugma"
aria-expanded={ochiq}
aria-controls={panelId}
onClick={() => boshla(() => setOchiq((o) => !o))}
>
Batafsil statistika
</button>
</h2>
<p id="batafsil-holat" role="status">
{kutilmoqda || eskirgan ? "Hisoblanmoqda…" : ""}
</p>
<Suspense
fallback={
<p id="batafsil-kutish">Statistika tayyorlanmoqda…</p>
}
>
<div
id={panelId}
hidden={!ochiq}
aria-busy={eskirgan || undefined}
>
{ochiq && <BatafsilStatistika vazifalar={kechikkan} />}
</div>
</Suspense>
</section>
);
}Bugungi darsning hamma g'oyasi shu faylda:
- Chegara shartdan tashqarida.
{ochiq && …}—<Suspense>ning ichida. «Chegara oldindan turishi kerak» bo'limidagi ikkinchi variantning aynan o'zi. Shuning uchun transition paytida fallback chiqmaydi. - Transition —
boshla(() => setOchiq(…)). Hisob tugaguncha panel yopiq qoladi,role="status"li qatorda "Hisoblanmoqda…" yoziladi. Malika buni eshitadi. useDeferredValue(useDeferredValue) — panel ochiq turganda katak bosilsa, ro'yxat yangi. Yangi ro'yxat uchun hisob yangidan boshlanadi —kechikkanesa eski ro'yxatni ushlab turadi. Ekranda eski statistika qoladi,aria-busybilan, yangisi tayyor bo'lgach almashadi. Bu ham transition — fallback yana chiqmaydi.- Fallback baribir bor. Transition'siz to'xtash bo'lsa (kelajakda kimdir
boshlani olib tashlasa), ilova oq bo'lib qolmaydi.
BatafsilStatistika.tsx — tayyor natijani jadvalga chizadi. Uning bosh qismi (kanon):
export function BatafsilStatistika({ vazifalar }: Props) {
// Promise keshdan (statistika-vadasi.ts): renderda yangisi
// yaratilmaydi
const s = use(statistikaVadasi(vazifalar));statistikaVadasi va undagi kesh — keyingi darsning asosiy mavzusi. Hozircha ikkala faylni (BatafsilStatistika.tsx, statistika-vadasi.ts) kanondagidek qo'shing. Ularni keyingi darsda qatorma-qator ochamiz.
App.tsx ga ikki qator:
+import { StatistikaPaneli } from "./komponentlar/StatistikaPaneli.tsx";
...
+ <StatistikaPaneli vazifalar={vazifalar} />
<p id="elon" role="status">vazifalar.css ga — 13 qator panel uslubi (sarlavhadagi tugma, jadval chiziqlari, raqamlar o'ngda).
8.4 Haqiqiy brauzerda nima bo'ldi
Kanonning brauzer tekshiruvi (2 000 vazifa, Chrome 155) tugma bosilgandan keyin har DOM o'zgarishini yozib oldi. Format — aria-expanded|holat matni:
false| → false|Hisoblanmoqda… → true|Yopiq panel → yopiq panel va "Hisoblanmoqda…" → ochiq panel, holat bo'sh. Fallback bir marta ham ko'rinmadi — tekshiruv uni alohida sanadi: 0. Katak bosilganda ham 0 — eski statistika turdi, yangisi fonda hisoblandi. Umumiy satr: 2000 ta vazifa, 667 tasi bajarilgan (33%). O'rtacha uzunlik — 17 belgi.
Hozircha commit qilmaymiz — qadam keyingi darsda tugaydi.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| butun ilova oq, keyin birdan chiqadi | use/lazy tepasida <Suspense> yo'q |
chegarani to'xtaydigan komponentga yaqin qo'ying |
| fallback hech qachon chiqmaydi | ma'lumot useEffect da olinadi |
use(promise) yoki Suspense'li kutubxona |
| tez qism sekinni kutib qoladi | ikkalasi bitta chegarada | sekin qismga ichki <Suspense> |
| tab almashganda kontent "Yuklanmoqda…" ga almashadi | yangilanish transition emas | startTransition(() => setTab(…)) |
| transition bor, fallback baribir chiqadi | <Suspense> shart ichida, yangi paydo bo'ladi |
chegarani shartdan tashqariga |
| "Yuklanmoqda…" abadiy, konsolda Promise'lar ko'payadi | Promise render ichida yaratilgan | Promise tashqarida yoki keshda (use()) |
10. Mashqlar
Mashqlar kurs/mashqlar/18/03-suspense/ da. Har yechimni «Ishga tushir» bilan sinang.
1-mashq (oson): qaysi parda yopiq?
Kodni o'qing (o'zgartirmang) va savollarga javob bering. «Ishga tushir» bilan tekshirishingiz mumkin: oynani diqqat bilan kuzating.
import { Suspense, use } from "react";
function wait(ms, value) {
return new Promise((resolve) => {
setTimeout(() => resolve(value), ms);
});
}
const menuPromise = wait(200, "Osh, Lag'mon, Manti");
const reviewsPromise = wait(600, "2 ta sharh");
function Menu() {
return <p>{use(menuPromise)}</p>;
}
function Reviews() {
return <p>{use(reviewsPromise)}</p>;
}
export default function App() {
return (
<Suspense fallback={<p>Sahifa yuklanmoqda…</p>}>
<Menu />
<Suspense fallback={<p>Sharhlar yuklanmoqda…</p>}>
<Reviews />
</Suspense>
</Suspense>
);
}- 100 ms da ekranda nechta fallback ko'rinadi?
- 400 ms da qaysi chegaraning fallback'i ko'rinadi — tashqi yoki ichki?
- 800 ms da nechta fallback qoladi?
Yechim
- 100 ms: bitta — "Sahifa yuklanmoqda…".
Menuhali to'xtagan, demak tashqi chegara yopiq. Ichki fallback uning orqasida — ko'rinmaydi. - 400 ms: menyu 200 ms da keldi, tashqi parda ochildi.
Reviewshali kutyapti — ekranda menyu va "Sharhlar yuklanmoqda…". - 800 ms: ikkalasi ham tayyor — fallback yo'q: "Osh, Lag'mon, Manti" va "2 ta sharh".
Uchala holat ham sinab ko'rildi. Ichki chegara faqat tashqisi ochilgandan keyin "ishga tushadi".
2-mashq (o'rta): menyu sharhlarni kutmasin
Sahifa butunlay bitta chegarada. Menyu 200 ms da tayyor, lekin sharhlar (800 ms) uni ushlab turibdi. Kodni o'zgartiring: menyu 200 ms da chiqsin, sharhlar o'rnida "Sharhlar yuklanmoqda…" tursin. Ishora: «Ichki chegara — bo'lak-bo'lak» bo'limi.
import { Suspense, use } from "react";
function wait(ms, value) {
return new Promise((resolve) => {
setTimeout(() => resolve(value), ms);
});
}
const menuPromise = wait(200, "Menyu: Osh, Lag'mon, Manti");
const reviewsPromise = wait(800, "Sharhlar: 2 ta");
function Menu() {
return <p>{use(menuPromise)}</p>;
}
function Reviews() {
return <p>{use(reviewsPromise)}</p>;
}
export default function App() {
return (
<Suspense fallback={<p>Yuklanmoqda…</p>}>
<Menu />
<Reviews />
</Suspense>
);
}Ekranda (400 ms da):
Yuklanmoqda…Yechim
import { Suspense, use } from "react";
function wait(ms, value) {
return new Promise((resolve) => {
setTimeout(() => resolve(value), ms);
});
}
const menuPromise = wait(200, "Menyu: Osh, Lag'mon, Manti");
const reviewsPromise = wait(800, "Sharhlar: 2 ta");
function Menu() {
return <p>{use(menuPromise)}</p>;
}
function Reviews() {
return <p>{use(reviewsPromise)}</p>;
}
export default function App() {
return (
<Suspense fallback={<p>Yuklanmoqda…</p>}>
<Menu />
<Suspense fallback={<p>Sharhlar yuklanmoqda…</p>}>
<Reviews />
</Suspense>
</Suspense>
);
}Ekranda (400 ms da):
Menyu: Osh, Lag'mon, Manti
Sharhlar yuklanmoqda…Reviews endi ichki chegarani to'xtatadi, tashqisiga tegmaydi. Tashqi chegarada faqat Menu qoldi — u 200 ms da tayyor. Tashqi fallback qolsinmi? Ha: Menu ham kutadi, uning ham pardasi kerak.
3-mashq (qiyin): tab almashganda eski kontent qolsin
Tablar har almashganda menyu "Yuklanmoqda…" ga almashyapti. Uni tuzating:
- Tab almashishi transition bo'lsin — kutish paytida eski tab kontenti ko'rinib tursin.
- Kutish paytida tablar ostida
<p>da "Kuting…" yozilsin, kutish yo'q paytda<p>bo'sh qolsin. - Fallback faqat sahifa birinchi ochilganda chiqsin.
Ishora: useTransition — const [isPending, startTransition] = useTransition();.
import { Suspense, use, useEffect, useState } from "react";
const DATA = {
taomlar: "Osh, Lag'mon, Manti",
ichimliklar: "Ko'k choy — 5000 so'm",
};
const cache = new Map();
function fetchTab(tab) {
if (!cache.has(tab)) {
const promise = new Promise((resolve) => {
setTimeout(() => resolve(DATA[tab]), 400);
});
cache.set(tab, promise);
}
return cache.get(tab);
}
function TabContent({ tab }) {
return <p>{use(fetchTab(tab))}</p>;
}
function Loading() {
useEffect(() => console.log("fallback ekranga chiqdi"), []);
return <p>Yuklanmoqda…</p>;
}
export default function App() {
const [tab, setTab] = useState("taomlar");
return (
<div>
<button onClick={() => setTab("taomlar")}>Taomlar</button>{" "}
<button onClick={() => setTab("ichimliklar")}>
Ichimliklar
</button>
<Suspense fallback={<Loading />}>
<TabContent tab={tab} />
</Suspense>
</div>
);
}Konsolda:
fallback ekranga chiqdi
fallback ekranga chiqdiYechim
import {
Suspense, use, useEffect, useState, useTransition,
} from "react";
const DATA = {
taomlar: "Osh, Lag'mon, Manti",
ichimliklar: "Ko'k choy — 5000 so'm",
};
const cache = new Map();
function fetchTab(tab) {
if (!cache.has(tab)) {
const promise = new Promise((resolve) => {
setTimeout(() => resolve(DATA[tab]), 400);
});
cache.set(tab, promise);
}
return cache.get(tab);
}
function TabContent({ tab }) {
return <p>{use(fetchTab(tab))}</p>;
}
function Loading() {
useEffect(() => console.log("fallback ekranga chiqdi"), []);
return <p>Yuklanmoqda…</p>;
}
export default function App() {
const [tab, setTab] = useState("taomlar");
const [isPending, startTransition] = useTransition();
function selectTab(next) {
startTransition(() => setTab(next));
}
return (
<div>
<button onClick={() => selectTab("taomlar")}>
Taomlar
</button>{" "}
<button onClick={() => selectTab("ichimliklar")}>
Ichimliklar
</button>
<p>{isPending ? "Kuting…" : ""}</p>
<Suspense fallback={<Loading />}>
<TabContent tab={tab} />
</Suspense>
</div>
);
}Konsolda:
fallback ekranga chiqdiEkranda (bosgandan 100 ms keyin):
Taomlar Ichimliklar
Kuting…
Osh, Lag'mon, MantiFallback bir marta — ochilishda. «Ichimliklar» bosilgach, taomlar joyida qoldi, "Kuting…" chiqdi. 400 ms dan keyin choy narxi chiqadi. Chegara sahifa ochilgandan beri ekranda — shuning uchun transition uni fallback'ga qaytarmadi. <p> doim joyida turibdi, faqat matni o'zgaradi. "Kuting…" paydo bo'lganda tablar ostidagi kontent pastga sakramaydi.
4-mashq: Vazifalar qadami
feat/batafsil-statistika branch'ida «Vazifalar qadami» bo'limidagi fayllarni qo'shing: batafsil-statistika.ts va uning testi, StatistikaPaneli.tsx, BatafsilStatistika.tsx, statistika-vadasi.ts, royxat.ts dagi eksportlar, App.tsx dagi ikki qator va CSS. Commit hali qilinmaydi.
Yechim
Tekshiruv ro'yxati:
npm test— node:test 160 ta (oldin 156, +4 batafsil statistika). Vitest panel testi keyingi darsda qo'shiladi.npm run dev— «Batafsil statistika» ni bosing: bir lahza "Hisoblanmoqda…", keyin jadval. "Statistika tayyorlanmoqda…" (fallback) ko'rinmasligi kerak.- Panel ochiq turganda vazifa katagini bosing — jadval yangilanadi, fallback yana chiqmaydi.
grep -n "Suspense" src/komponentlar/StatistikaPaneli.tsx— chegara{ochiq && …}dan tashqarida.
11. Real ishda
- Next.js sahifasidagi
loading.tsxfayli — aslida sahifa atrofidagi<Suspense>chegarasi (Maxsus fayllar). Server komponentlar sahifani bo'lak-bo'lak "oqim" bilan yuboradi — har chegara o'z vaqtida ochiladi (Streaming va Suspense). - TanStack Query ning
useSuspenseQuerysi, Relay va Apollo — Suspense bilan ishlaydigan ma'lumot kutubxonalari. Ular ichida kesh bor (TanStack Query I). - Router'lar sahifa almashishini transition qiladi — yangi sahifa tayyor bo'lguncha eskisi turadi. Bugungi «Transition bilan fallback'ni yashirish» bo'limi shuning o'zi.
- Dizayn tizimlarida skeleton komponentlari tayyor turadi; chegara joylashuvi — dizayner va dasturchi birga qiladigan qaror.
- Intervyu savollari: "Suspense qanday ishlaydi?", "Suspense
useEffectdagi fetch'ni ushlaydimi?", "fallback miltillashini qanday oldini olasiz?", "chegaralarni qanday joylashtirasiz?".
Xulosa
<Suspense fallback>— kutish holatining bitta deklarativ joyi. Komponent faqat tayyor ma'lumotni chizadi.- Suspend qiladi:
use(promise),lazy, Suspense'li framework va kutubxonalar.useEffectdagifetch— yo'q. - To'xtagan komponent eng yaqin chegarani yopadi. Chegara yo'q bo'lsa — butun ildiz.
- Bitta chegara — hamma birga; ichki chegara — bo'lak-bo'lak. Joylashuv — UX qarori.
- Transition ko'rinib turgan chegarani fallback'ga qaytarmaydi. Shuning uchun
<Suspense>shartdan tashqarida tursin. - Ma'lumot allaqachon bor joyda Suspense to'sqinlik qiladi.
vazifalarro'yxati shuning uchun Suspense'siz: o'lchovda 40–160 ms bekor kutish chiqdi.
Keyingi dars: use(): promise va context'ni o'qish — use Promise'ni qanday kutadi, nega Promise render ichida yaratilmaydi va vazifalar dagi Promise keshi qanday ishlaydi.
Manbalar
- React:
<Suspense>— react.dev/reference/react/Suspense (2026-10 holatiga, React 19.3) - React:
use— react.dev/reference/react/use - React:
lazy— react.dev/reference/react/lazy - React 19 e'loni — react.dev/blog/2024/12/05/react-19
- React 19.3.0 manba kodi,
react-dom-client.development.js:FALLBACK_THROTTLE_MS = 300(fallback ushlab turiladigan vaqt), Prewarm
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!