Kod bo'lishdan tashqari ma'lumotlarni olish uchun React Suspense'ni o'rganing. Render-qilish-bilan-birga-yuklash, xatolarni boshqarish va global ilovalar uchun kelajakka yo'naltirilgan uslublarni tushuning.
React Suspense Resurslarini Yuklash: Zamonaviy Ma'lumotlarni Olish Uslublarini O'zlashtirish
Veb-dasturlashning dinamik dunyosida foydalanuvchi tajribasi (UX) eng muhim o'rinni egallaydi. Ilovalar tarmoq sharoitlari yoki qurilma imkoniyatlaridan qat'i nazar, tez, sezgir va yoqimli bo'lishi kutiladi. React dasturchilari uchun bu ko'pincha murakkab holat boshqaruvi, murakkab yuklanish ko'rsatkichlari va ma'lumotlarni olish "sharshara"lariga qarshi doimiy kurashni anglatadi. Asenkron operatsiyalarni, ayniqsa ma'lumotlarni olishni qanday boshqarishimizni tubdan o'zgartirish uchun mo'ljallangan kuchli, ammo ko'pincha noto'g'ri tushuniladigan React Suspense xususiyati sahnaga chiqadi.
Dastlab React.lazy()
bilan kod bo'lish uchun taqdim etilgan Suspense'ning haqiqiy salohiyati uning *har qanday* asenkron resursni, jumladan API'dan olingan ma'lumotlarni yuklashni tashkil qilish qobiliyatida yotadi. Ushbu keng qamrovli qo'llanma React Suspense'ning resurslarni yuklash uchun chuqur o'rganadi, uning asosiy tushunchalarini, fundamental ma'lumotlarni olish uslublarini va yuqori unumdorlikka ega va barqaror global ilovalarni yaratish uchun amaliy mulohazalarni o'rganadi.
React'da Ma'lumotlarni Olish Evolyutsiyasi: Imperativdan Deklarativgacha
Ko'p yillar davomida React komponentlarida ma'lumotlarni olish asosan umumiy bir uslubga tayangan: API so'rovini boshlash uchun useEffect
hookidan foydalanish, yuklanish va xatolik holatlarini useState
bilan boshqarish va shu holatlarga qarab shartli render qilish. Funktsional bo'lishiga qaramay, bu yondashuv ko'pincha bir nechta muammolarga olib keldi:
- Yuklanish Holatining Ko'payishi: Ma'lumot talab qiladigan deyarli har bir komponent o'zining
isLoading
,isError
vadata
holatlariga muhtoj bo'lib, bu takrorlanuvchi kodlarga olib keldi. - "Sharshara"lar va Poyga Sharoitlari: Ma'lumotlarni oluvchi ichki komponentlar ko'pincha ketma-ket so'rovlarga ("sharshara"larga) olib keldi, bunda ota-komponent ma'lumotlarni oladi, keyin render qiladi, so'ngra bola-komponent o'z ma'lumotlarini oladi va hokazo. Bu umumiy yuklanish vaqtini oshirdi. Shuningdek, bir nechta so'rovlar boshlanganida va javoblar tartibsiz kelganida poyga sharoitlari yuzaga kelishi mumkin edi.
- Murakkab Xatolarni Boshqarish: Xato xabarlarini va tiklash mantiqini ko'plab komponentlar bo'ylab tarqatish, prop drilling yoki global holat boshqaruvi yechimlarini talab qilib, noqulay bo'lishi mumkin edi.
- Yoqimsiz Foydalanuvchi Tajribasi: Bir nechta spinnerlarning paydo bo'lishi va yo'qolishi yoki kontentning keskin siljishi (layout shifts) foydalanuvchilar uchun notekis tajriba yaratishi mumkin edi.
- Ma'lumotlar va Holatlar uchun Prop Drilling: Olingan ma'lumotlar va tegishli yuklanish/xato holatlarini bir necha komponent darajasi orqali uzatish murakkablikning umumiy manbasiga aylandi.
Suspense'siz odatiy ma'lumotlarni olish stsenariysini ko'rib chiqing:
import React, { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUser = async () => {
try {
setIsLoading(true);
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP xatosi! status: ${response.status}`);
}
const data = await response.json();
setUser(data);
} catch (e) {
setError(e);
} finally {
setIsLoading(false);
}
};
fetchUser();
}, [userId]);
if (isLoading) {
return <p>Foydalanuvchi profilini yuklash...</p>;
}
if (error) {
return <p style={"color: red;"}>Xatolik: {error.message}</p>;
}
if (!user) {
return <p>Foydalanuvchi ma'lumotlari mavjud emas.</p>;
}
return (
<div>
<h2>Foydalanuvchi: {user.name}</h2>
<p>Email: {user.email}</p>
<!-- Qo'shimcha foydalanuvchi ma'lumotlari -->
</div>
);
}
function App() {
return (
<div>
<h1>Ilovaga xush kelibsiz</h1>
<UserProfile userId={"123"} />
</div>
);
}
Bu uslub hamma joyda mavjud, ammo u komponentni o'zining asenkron holatini boshqarishga majbur qiladi, bu esa ko'pincha UI va ma'lumotlarni olish mantig'i o'rtasida mustahkam bog'liqlikka olib keladi. Suspense esa yanada deklarativ va soddalashtirilgan muqobilni taklif etadi.
React Suspense'ni Kod Bo'lishdan Tashqari Tushunish
Ko'pchilik dasturchilar Suspense bilan birinchi marta React.lazy()
orqali kod bo'lishda tanishadilar, bu yerda u komponent kodini kerak bo'lgunga qadar yuklashni kechiktirishga imkon beradi. Masalan:
import React, { Suspense, lazy } from 'react';
const LazyComponent = lazy(() => import('./MyHeavyComponent'));
function App() {
return (
<Suspense fallback={<div>Komponent yuklanmoqda...</div>}>
<LazyComponent />
</Suspense>
);
}
Bu stsenariyda, agar MyHeavyComponent
hali yuklanmagan bo'lsa, <Suspense>
chegarasi lazy()
tomonidan tashlangan promisen'i ushlaydi va komponent kodi tayyor bo'lguncha fallback
'ni ko'rsatadi. Bu yerdagi asosiy tushuncha shuki, Suspense render paytida tashlangan promiselarni ushlash orqali ishlaydi.
Bu mexanizm faqat kod yuklashga xos emas. Render paytida chaqiriladigan va promise tashlaydigan har qanday funksiya (masalan, resurs hali mavjud bo'lmaganligi sababli) komponentlar iyerarxiyasida yuqoriroqda joylashgan Suspense chegarasi tomonidan ushlanishi mumkin. Promise bajarilganda, React komponentni qayta render qilishga harakat qiladi va agar resurs endi mavjud bo'lsa, fallback yashiriladi va haqiqiy kontent ko'rsatiladi.
Ma'lumotlarni Olish uchun Suspense'ning Asosiy Tushunchalari
Ma'lumotlarni olish uchun Suspense'dan foydalanish uchun biz bir nechta asosiy tamoyillarni tushunishimiz kerak:
1. Promise Tashlash
Promise'larni hal qilish uchun async/await
dan foydalanadigan an'anaviy asenkron koddan farqli o'laroq, Suspense ma'lumotlar tayyor bo'lmasa, promise *tashlaydigan* funksiyaga tayanadi. React bunday funksiyani chaqiradigan komponentni render qilishga harakat qilganda va ma'lumotlar hali kutilayotgan bo'lsa, promise tashlanadi. Keyin React ushbu komponent va uning bolalarini render qilishni "to'xtatib turadi" va eng yaqin <Suspense>
chegarasini qidiradi.
2. Suspense Chegarasi
<Suspense>
komponenti promiselar uchun xatolik chegarasi vazifasini bajaradi. U fallback
prop'ini qabul qiladi, bu uning bolalaridan (yoki ularning avlodlaridan) birortasi to'xtatib turilganda (ya'ni, promise tashlaganda) render qilinadigan UI'dir. Uning iyerarxiyasidagi barcha tashlangan promiselar bajarilgach, fallback haqiqiy kontent bilan almashtiriladi.
Bitta Suspense chegarasi bir nechta asenkron operatsiyalarni boshqarishi mumkin. Masalan, agar sizda bir xil <Suspense>
chegarasi ichida ikkita komponent bo'lsa va har biri ma'lumotlarni olishi kerak bo'lsa, fallback *har ikkala* ma'lumotlarni olish tugallanmaguncha ko'rsatiladi. Bu qisman UI ko'rsatilishining oldini oladi va yanada muvofiqlashtirilgan yuklanish tajribasini ta'minlaydi.
3. Kesh/Resurs Menejeri (Foydalanuvchi Tomonidagi Mas'uliyat)
Muhimi shundaki, Suspense o'zi ma'lumotlarni olish yoki keshlash bilan shug'ullanmaydi. Bu shunchaki muvofiqlashtirish mexanizmi. Suspense'ni ma'lumotlarni olish uchun ishlatish uchun sizga quyidagi vazifalarni bajaradigan bir qatlam kerak:
- Ma'lumotlarni olishni boshlaydi.
- Natijani (hal qilingan ma'lumotlar yoki kutilayotgan promise'ni) keshlaydi.
- Sinxron
read()
usulini taqdim etadi, u yoki keshdagi ma'lumotlarni darhol qaytaradi (agar mavjud bo'lsa) yoki kutilayotgan promise'ni tashlaydi (agar mavjud bo'lmasa).
Ushbu "resurs menejeri" odatda har bir resursning holatini (kutilayotgan, hal qilingan yoki xatolik) saqlash uchun oddiy kesh (masalan, Map yoki obyekt) yordamida amalga oshiriladi. Buni namoyish qilish uchun qo'lda yaratishingiz mumkin bo'lsa-da, real dasturda siz Suspense bilan integratsiyalashgan mustahkam ma'lumotlarni olish kutubxonasidan foydalanasiz.
4. Concurrent Mode (React 18 Yaxshilanishlari)
Suspense'ni React'ning eski versiyalarida ishlatish mumkin bo'lsa-da, uning to'liq kuchi Concurrent React bilan ochiladi (React 18 da createRoot
bilan standart tarzda yoqilgan). Concurrent Mode React'ga render ishini to'xtatish, pauza qilish va davom ettirishga imkon beradi. Bu quyidagilarni anglatadi:
- Bloklanmaydigan UI Yangilanishlari: Suspense fallback ko'rsatganda, React to'xtatilmagan UI'ning boshqa qismlarini render qilishni davom ettirishi yoki hatto asosiy thread'ni bloklamasdan orqa fonda yangi UI'ni tayyorlashi mumkin.
- O'tishlar (Transitions):
useTransition
kabi yangi API'lar sizga ma'lum yangilanishlarni "o'tishlar" deb belgilash imkonini beradi, ularni React to'xtatib, kamroq shoshilinch qilib qo'yishi mumkin, bu esa ma'lumotlarni olish paytida silliqroq UI o'zgarishlarini ta'minlaydi.
Suspense bilan Ma'lumotlarni Olish Uslublari
Keling, Suspense paydo bo'lishi bilan ma'lumotlarni olish uslublarining evolyutsiyasini o'rganamiz.
1-uslub: Yuklash-Keyin-Render (An'anaviy, Suspense bilan o'ralgan)
Bu klassik yondashuv bo'lib, unda ma'lumotlar olinadi va shundan keyingina komponent render qilinadi. Ma'lumotlar uchun to'g'ridan-to'g'ri "promise tashlash" mexanizmidan foydalanmasada, siz *oxir-oqibat* ma'lumotlarni render qiladigan komponentni Suspense chegarasi bilan o'rab, fallback taqdim etishingiz mumkin. Bu ko'proq Suspense'ni oxir-oqibat tayyor bo'ladigan komponentlar uchun umumiy yuklanish UI orkestratori sifatida ishlatish haqida, hatto ularning ichki ma'lumotlarni olishi hali ham an'anaviy useEffect
ga asoslangan bo'lsa ham.
import React, { Suspense, useState, useEffect } from 'react';
function UserDetails({ userId }) {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const fetchUserData = async () => {
setIsLoading(true);
const res = await fetch(`/api/users/${userId}`);
const data = await res.json();
setUser(data);
setIsLoading(false);
};
fetchUserData();
}, [userId]);
if (isLoading) {
return <p>Foydalanuvchi ma'lumotlarini yuklash...</p>;
}
return (
<div>
<h3>Foydalanuvchi: {user.name}</h3>
<p>Email: {user.email}</p>
</div>
);
}
function App() {
return (
<div>
<h1>Yuklash-Keyin-Render Misoli</h1>
<Suspense fallback={<div>Umumiy sahifa yuklanmoqda...</div>}>
<UserDetails userId={"1"} />
</Suspense>
</div>
);
}
Afzalliklari: Tushunish oson, orqaga mos keladi. Global yuklanish holatini tezda qo'shish uchun ishlatilishi mumkin.
Kamchiliklari: UserDetails
ichidagi takrorlanuvchi kodni yo'q qilmaydi. Komponentlar ma'lumotlarni ketma-ket olsa, hali ham "sharshara"larga moyil. Ma'lumotlarning o'zi uchun Suspense'ning "tashlash-va-ushlash" mexanizmidan to'liq foydalanmaydi.
2-uslub: Render-Keyin-Yuklash (Render ichida yuklash, production uchun emas)
Bu uslub asosan Suspense bilan to'g'ridan-to'g'ri nima qilmaslik kerakligini ko'rsatish uchun mo'ljallangan, chunki u ehtiyotkorlik bilan ishlanmasa, cheksiz tsikllarga yoki unumdorlik muammolariga olib kelishi mumkin. Bu komponentning render fazasida to'g'ridan-to'g'ri ma'lumotlarni olishga yoki to'xtatuvchi funksiyani chaqirishga urinishni o'z ichiga oladi, *to'g'ri keshlash mexanizmisiz*.
// TO'G'RI KESHLASH QATLAMI BO'LMASA PRODUCTION'DA FOYDALANMANG
// Bu shunchaki to'g'ridan-to'g'ri 'tashlash' kontseptual jihatdan qanday ishlashini ko'rsatish uchun.
let fetchedData = null;
let dataPromise = null;
function fetchDataSynchronously(url) {
if (fetchedData) {
return fetchedData;
}
if (!dataPromise) {
dataPromise = fetch(url)
.then(res => res.json())
.then(data => { fetchedData = data; dataPromise = null; return data; })
.catch(err => { dataPromise = null; throw err; });
}
throw dataPromise; // Bu yerda Suspense ishga tushadi
}
function UserDetailsBadExample({ userId }) {
const user = fetchDataSynchronously(`/api/users/${userId}`);
return (
<div>
<h3>Foydalanuvchi: {user.name}</h3>
<p>Email: {user.email}</p>
</div>
);
}
function App() {
return (
<div>
<h1>Render-Keyin-Yuklash (Ko'rgazmali, To'g'ridan-to'g'ri Tavsiya Etilmaydi)</h1>
<Suspense fallback={<div>Foydalanuvchi yuklanmoqda...</div>}>
<UserDetailsBadExample userId={"2"} />
</Suspense>
</div>
);
}
Afzalliklari: Komponentning qanday qilib to'g'ridan-to'g'ri ma'lumot "so'rashi" va tayyor bo'lmasa to'xtab turishi mumkinligini ko'rsatadi.
Kamchiliklari: Production uchun juda muammoli. Bu qo'lda yozilgan, global fetchedData
va dataPromise
tizimi sodda, bir nechta so'rovlarni, bekor qilishni yoki xato holatlarini mustahkam boshqarmaydi. Bu qabul qilish kerak bo'lgan uslub emas, balki "promise-tashlash" kontseptsiyasining ibtidoiy tasviridir.
3-uslub: Render-qilish-bilan-birga-yuklash (Ideal Suspense Uslubi)
Bu Suspense ma'lumotlarni olish uchun haqiqatdan ham imkon beradigan paradigma o'zgarishidir. Komponentning ma'lumotlarini olishdan oldin render bo'lishini kutish o'rniga yoki barcha ma'lumotlarni oldindan olish o'rniga, Render-qilish-bilan-birga-yuklash siz ma'lumotlarni olishni *imkon qadar tezroq*, ko'pincha render jarayonidan *oldin* yoki u bilan *bir vaqtda* boshlashingizni anglatadi. Keyin komponentlar ma'lumotlarni keshdan "o'qiydi" va agar ma'lumotlar tayyor bo'lmasa, ular to'xtab turadi. Asosiy g'oya ma'lumotlarni olish mantig'ini komponentning render mantig'idan ajratishdir.
Render-qilish-bilan-birga-yuklashni amalga oshirish uchun sizga quyidagi imkoniyatlarni beruvchi mexanizm kerak:
- Komponentning render funksiyasidan tashqarida ma'lumotlarni olishni boshlash (masalan, marshrutga kirilganda yoki tugma bosilganda).
- Promise yoki hal qilingan ma'lumotlarni keshda saqlash.
- Komponentlarga ushbu keshdan "o'qish" usulini taqdim etish. Agar ma'lumotlar hali mavjud bo'lmasa, o'qish funksiyasi kutilayotgan promise'ni tashlaydi.
Bu uslub "sharshara" muammosini hal qiladi. Agar ikkita turli komponent ma'lumotlarga muhtoj bo'lsa, ularning so'rovlari parallel ravishda boshlanishi mumkin va UI faqat *har ikkisi* ham tayyor bo'lgandagina paydo bo'ladi, bu bitta Suspense chegarasi tomonidan boshqariladi.
Qo'lda Amalga Oshirish (Tushunish Uchun)
Asosiy mexanikalarni tushunish uchun keling, soddalashtirilgan qo'lda resurs menejerini yaratamiz. Haqiqiy dasturda siz maxsus kutubxonadan foydalanasiz.
import React, { Suspense } from 'react';
// --- Sodda Kesh/Resurs Menejeri --- //
const cache = new Map();
function createResource(promise) {
let status = 'pending';
let result;
let suspender = promise.then(
(r) => {
status = 'success';
result = r;
},
(e) => {
status = 'error';
result = e;
}
);
return {
read() {
if (status === 'pending') {
throw suspender;
} else if (status === 'error') {
throw result;
} else if (status === 'success') {
return result;
}
},
};
}
function fetchData(key, fetcher) {
if (!cache.has(key)) {
cache.set(key, createResource(fetcher()));
}
return cache.get(key);
}
// --- Ma'lumotlarni Olish Funksiyalari --- //
const fetchUserById = (id) => {
console.log(`Foydalanuvchi ${id} olinmoqda...`);
return new Promise(resolve => setTimeout(() => {
const users = {
'1': { id: '1', name: 'Alice Smith', email: 'alice@example.com' },
'2': { id: '2', name: 'Bob Johnson', email: 'bob@example.com' },
'3': { id: '3', name: 'Charlie Brown', email: 'charlie@example.com' }
};
resolve(users[id]);
}, 1500));
};
const fetchPostsByUserId = (userId) => {
console.log(`${userId} foydalanuvchisi uchun postlar olinmoqda...`);
return new Promise(resolve => setTimeout(() => {
const posts = {
'1': [{ id: 'p1', title: 'Mening Birinchi Postim' }, { id: 'p2', title: 'Sayohat Sarguzashtlari' }],
'2': [{ id: 'p3', title: 'Dasturlash Haqida Fikrlar' }],
'3': [{ id: 'p4', title: 'Global Trendlar' }, { id: 'p5', title: 'Mahalliy Taomlar' }]
};
resolve(posts[userId] || []);
}, 2000));
};
// --- Komponentlar --- //
function UserProfile({ userId }) {
const userResource = fetchData(`user-${userId}`, () => fetchUserById(userId));
const user = userResource.read(); // Agar foydalanuvchi ma'lumotlari tayyor bo'lmasa, bu to'xtatiladi
return (
<div>
<h3>Foydalanuvchi: {user.name}</h3>
<p>Email: {user.email}</p>
</div>
);
}
function UserPosts({ userId }) {
const postsResource = fetchData(`posts-${userId}`, () => fetchPostsByUserId(userId));
const posts = postsResource.read(); // Agar postlar ma'lumotlari tayyor bo'lmasa, bu to'xtatiladi
return (
<div>
<h4>{userId} postlari:</h4>
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
{posts.length === 0 && <li>Postlar topilmadi.</li>}
</ul>
</div>
);
}
// --- Ilova --- //
let initialUserResource = null;
let initialPostsResource = null;
function prefetchDataForUser(userId) {
initialUserResource = fetchData(`user-${userId}`, () => fetchUserById(userId));
initialPostsResource = fetchData(`posts-${userId}`, () => fetchPostsByUserId(userId));
}
// App komponenti render bo'lishidan oldin ba'zi ma'lumotlarni oldindan yuklash
prefetchDataForUser('1');
function App() {
return (
<div>
<h1>Suspense bilan Render-qilish-bilan-birga-yuklash</h1>
<p>Bu ma'lumotlarni olish Suspense tomonidan muvofiqlashtirilgan holda parallel ravishda qanday sodir bo'lishini ko'rsatadi.</p>
<Suspense fallback={<div>Foydalanuvchi profili va postlar yuklanmoqda...</div>}>
<UserProfile userId={"1"} />
<UserPosts userId={"1"} />
</Suspense>
<h2>Boshqa Bo'lim</h2>
<Suspense fallback={<div>Boshqa foydalanuvchi yuklanmoqda...</div>}>
<UserProfile userId={"2"} />
</Suspense>
</div>
);
}
Ushbu misolda:
createResource
vafetchData
funksiyalari asosiy keshlash mexanizmini yaratadi.UserProfile
yokiUserPosts
resource.read()
ni chaqirganda, ular yoki darhol ma'lumotlarni oladi yoki promise tashlanadi.- Eng yaqin
<Suspense>
chegarasi promise(lar)ni ushlaydi va o'zining fallback'ini ko'rsatadi. - Muhimi, biz
prefetchDataForUser('1')
niApp
komponenti render bo'lishidan *oldin* chaqirishimiz mumkin, bu esa ma'lumotlarni olishni yanada ertaroq boshlashga imkon beradi.
Render-qilish-bilan-birga-yuklash uchun Kutubxonalar
Mustahkam resurs menejerini qo'lda yaratish va qo'llab-quvvatlash murakkab. Yaxshiyamki, bir nechta yetuk ma'lumotlarni olish kutubxonalari Suspense'ni qabul qilgan yoki qabul qilmoqda, ular sinovdan o'tgan yechimlarni taqdim etadi:
- React Query (TanStack Query): Suspense'ni qo'llab-quvvatlaydigan kuchli ma'lumotlarni olish va keshlash qatlamini taklif etadi. U
useQuery
kabi to'xtatishi mumkin bo'lgan hooklarni taqdim etadi. REST API'lar uchun a'lo darajada. - SWR (Stale-While-Revalidate): Suspense'ni to'liq qo'llab-quvvatlaydigan yana bir mashhur va yengil ma'lumotlarni olish kutubxonasi. REST API'lar uchun ideal, u ma'lumotlarni tezda taqdim etishga (eskirgan) va keyin ularni orqa fonda qayta tekshirishga e'tibor qaratadi.
- Apollo Client: GraphQL so'rovlari va mutatsiyalari uchun mustahkam Suspense integratsiyasiga ega bo'lgan keng qamrovli GraphQL mijozi.
- Relay: Facebook'ning o'z GraphQL mijozi, boshidanoq Suspense va Concurrent React uchun mo'ljallangan. U maxsus GraphQL sxemasi va kompilyatsiya bosqichini talab qiladi, lekin misli ko'rilmagan unumdorlik va ma'lumotlar barqarorligini taklif etadi.
- Urql: Suspense'ni qo'llab-quvvatlaydigan yengil va yuqori darajada sozlanadigan GraphQL mijozi.
Ushbu kutubxonalar resurslarni yaratish va boshqarish, keshlash, qayta tekshirish, optimistik yangilanishlar va xatolarni boshqarish murakkabliklarini yashiradi, bu esa Render-qilish-bilan-birga-yuklashni amalga oshirishni ancha osonlashtiradi.
4-uslub: Suspense'ni biladigan kutubxonalar bilan oldindan yuklash
Oldindan yuklash (Prefetching) kuchli optimallashtirish bo'lib, unda siz foydalanuvchining yaqin kelajakda kerak bo'lishi mumkin bo'lgan ma'lumotlarni, ular hali aniq so'ramaganlaridan oldin proaktiv ravishda yuklaysiz. Bu seziladigan unumdorlikni keskin yaxshilashi mumkin.
Suspense'ni biladigan kutubxonalar bilan oldindan yuklash muammosiz bo'ladi. Siz foydalanuvchining UI'ni darhol o'zgartirmaydigan o'zaro ta'sirlarida, masalan, havola ustiga sichqonchani olib borish yoki tugma ustiga olib borish kabi harakatlarda ma'lumotlarni yuklashni ishga tushirishingiz mumkin.
import React, { Suspense } from 'react';
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';
// Bular sizning API so'rovlaringiz deb faraz qiling
const fetchProductById = async (id) => {
console.log(`Mahsulot ${id} olinmoqda...`);
return new Promise(resolve => setTimeout(() => {
const products = {
'A001': { id: 'A001', name: 'Global Widget X', price: 29.99, description: 'Xalqaro foydalanish uchun ko'p qirrali vidjet.' },
'B002': { id: 'B002', name: 'Universal Gadget Y', price: 149.99, description: 'Butun dunyoda sevilib foydalaniladigan zamonaviy gadjet.' },
};
resolve(products[id]);
}, 1000));
};
const queryClient = new QueryClient({
defaultOptions: {
queries: {
suspense: true, // Barcha so'rovlar uchun Suspense'ni standart ravishda yoqish
},
},
});
function ProductDetails({ productId }) {
const { data: product } = useQuery({
queryKey: ['product', productId],
queryFn: () => fetchProductById(productId),
});
return (
<div style={{"border": "1px solid #ccc", "padding": "15px", "margin": "10px 0"}}>
<h3>{product.name}</h3>
<p>Narxi: ${product.price.toFixed(2)}</p>
<p>{product.description}</p>
</div>
);
}
function ProductList() {
const handleProductHover = (productId) => {
// Foydalanuvchi mahsulot havolasi ustiga sichqonchani olib borganda ma'lumotlarni oldindan yuklash
queryClient.prefetchQuery({
queryKey: ['product', productId],
queryFn: () => fetchProductById(productId),
});
console.log(`Mahsulot ${productId} oldindan yuklanmoqda`);
};
return (
<div>
<h2>Mavjud Mahsulotlar:</h2>
<ul>
<li>
<a href="#" onMouseEnter={() => handleProductHover('A001')}
onClick={(e) => { e.preventDefault(); /* Navigatsiya qilish yoki tafsilotlarni ko'rsatish */ }}
>Global Widget X (A001)</a>
</li>
<li>
<a href="#" onMouseEnter={() => handleProductHover('B002')}
onClick={(e) => { e.preventDefault(); /* Navigatsiya qilish yoki tafsilotlarni ko'rsatish */ }}
>Universal Gadget Y (B002)</a>
</li>
</ul>
<p>Oldindan yuklashni amalda ko'rish uchun mahsulot havolasi ustiga sichqonchani olib boring. Kuzatish uchun tarmoq yorlig'ini oching.</p>
</div>
);
}
function App() {
const [showProductA, setShowProductA] = React.useState(false);
const [showProductB, setShowProductB] = React.useState(false);
return (
<QueryClientProvider client={queryClient}>
<h1>React Suspense bilan oldindan yuklash (React Query)</h1>
<ProductList />
<button onClick={() => setShowProductA(true)}>Global Widget X'ni ko'rsatish</button>
<button onClick={() => setShowProductB(true)}>Universal Gadget Y'ni ko'rsatish</button>
{showProductA && (
<Suspense fallback={<p>Global Widget X yuklanmoqda...</p>}>
<ProductDetails productId="A001" />
</Suspense>
)}
{showProductB && (
<Suspense fallback={<p>Universal Gadget Y yuklanmoqda...</p>}>
<ProductDetails productId="B002" />
</Suspense>
)}
</QueryClientProvider>
);
}
Ushbu misolda, mahsulot havolasi ustiga sichqonchani olib borish `queryClient.prefetchQuery` ni ishga tushiradi, bu esa ma'lumotlarni orqa fonda yuklashni boshlaydi. Agar foydalanuvchi keyin mahsulot tafsilotlarini ko'rsatish uchun tugmani bossa va ma'lumotlar oldindan yuklashdan keshda allaqachon mavjud bo'lsa, komponent to'xtamasdan darhol render bo'ladi. Agar oldindan yuklash hali davom etayotgan bo'lsa yoki boshlanmagan bo'lsa, Suspense ma'lumotlar tayyor bo'lguncha fallback'ni ko'rsatadi.
Suspense va Xatolik Chegaralari bilan Xatolarni Boshqarish
Suspense fallback ko'rsatish orqali "yuklanish" holatini boshqarsa-da, u "xatolik" holatlarini to'g'ridan-to'g'ri boshqarmaydi. Agar to'xtatuvchi komponent tomonidan tashlangan promise rad etilsa (ya'ni, ma'lumotlarni olish muvaffaqiyatsiz bo'lsa), bu xato komponentlar iyerarxiyasi bo'ylab yuqoriga tarqaladi. Ushbu xatolarni chiroyli tarzda boshqarish va tegishli UI'ni ko'rsatish uchun siz Xatolik Chegaralari (Error Boundaries)'dan foydalanishingiz kerak.
Xatolik Chegarasi - bu componentDidCatch
yoki static getDerivedStateFromError
hayotiy sikl usullaridan birini amalga oshiradigan React komponentidir. U o'zining bola komponentlar iyerarxiyasidagi istalgan joyda JavaScript xatolarini ushlaydi, shu jumladan Suspense odatda kutilayotgan holatda ushlaydigan promiselar tomonidan tashlangan xatolarni ham.
import React, { Suspense, useState } from 'react';
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';
// --- Xatolik Chegarasi Komponenti --- //
class MyErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
// Keyingi renderda zaxira UI'ni ko'rsatish uchun holatni yangilang.
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
// Xatoni xatoliklar haqida xabar berish xizmatiga ham yozib qo'yishingiz mumkin
console.error("Xatolik ushlandi:", error, errorInfo);
}
render() {
if (this.state.hasError) {
// Siz istalgan maxsus zaxira UI'ni render qilishingiz mumkin
return (
<div style={{"border": "2px solid red", "padding": "20px", "margin": "20px 0", "background": "#ffe0e0"}}>
<h2>Nimadir noto'g'ri ketdi!</h2>
<p>{this.state.error && this.state.error.message}</p>
<p>Iltimos, sahifani yangilab ko'ring yoki qo'llab-quvvatlash xizmatiga murojaat qiling.</p>
<button onClick={() => this.setState({ hasError: false, error: null })}>Qayta urinib ko'ring</button>
</div>
);
}
return this.props.children;
}
}
// --- Ma'lumotlarni Olish (xatolik ehtimoli bilan) --- //
const fetchItemById = async (id) => {
console.log(`${id} elementini olishga urinilmoqda...`);
return new Promise((resolve, reject) => setTimeout(() => {
if (id === 'error-item') {
reject(new Error('Elementni yuklashda xatolik: Tarmoqqa ulanib bo\'lmadi yoki element topilmadi.'));
} else if (id === 'slow-item') {
resolve({ id: 'slow-item', name: 'Sekin Yetkazildi', data: 'Bu element biroz vaqt oldi, lekin yetib keldi!', status: 'success' });
} else {
resolve({ id, name: `Item ${id}`, data: `Data for item ${id}` });
}
}, id === 'slow-item' ? 3000 : 800));
};
const queryClient = new QueryClient({
defaultOptions: {
queries: {
suspense: true,
retry: false, // Namoyish uchun, xato darhol yuz berishi uchun qayta urinishni o'chiring
},
},
});
function DisplayItem({ itemId }) {
const { data: item } = useQuery({
queryKey: ['item', itemId],
queryFn: () => fetchItemById(itemId),
});
return (
<div>
<h3>Element Tafsilotlari:</h3>
<p>ID: {item.id}</p>
<p>Name: {item.name}</p>
<p>Data: {item.data}</p>
</div>
);
}
function App() {
const [fetchType, setFetchType] = useState('normal-item');
return (
<QueryClientProvider client={queryClient}>
<h1>Suspense va Xatolik Chegaralari</h1>
<div>
<button onClick={() => setFetchType('normal-item')}>Oddiy Elementni Olish</button>
<button onClick={() => setFetchType('slow-item')}>Sekin Elementni Olish</button>
<button onClick={() => setFetchType('error-item')}>Xatolik Elementini Olish</button>
</div>
<MyErrorBoundary>
<Suspense fallback={<p>Element Suspense orqali yuklanmoqda...</p>}>
<DisplayItem itemId={fetchType} />
</Suspense>
</MyErrorBoundary>
</QueryClientProvider>
);
}
Suspense chegarangizni (yoki to'xtab qolishi mumkin bo'lgan komponentlarni) Xatolik Chegarasi bilan o'rash orqali siz ma'lumotlarni olish paytida tarmoq nosozliklari yoki server xatolari ushlanib, chiroyli tarzda boshqarilishini ta'minlaysiz, bu butun dasturning ishdan chiqishini oldini oladi. Bu mustahkam va foydalanuvchilar uchun qulay tajriba taqdim etadi, foydalanuvchilarga muammoni tushunish va ehtimol qayta urinib ko'rish imkonini beradi.
Suspense bilan Holat Boshqaruvi va Ma'lumotlarni Bekor Qilish
Shuni aniqlab olish muhimki, React Suspense asosan asenkron resurslarning dastlabki yuklanish holatini boshqaradi. U o'z-o'zidan mijoz tomonidagi keshni boshqarmaydi, ma'lumotlarni bekor qilish bilan shug'ullanmaydi yoki mutatsiyalarni (yaratish, yangilash, o'chirish operatsiyalari) va ularning keyingi UI yangilanishlarini tashkil qilmaydi.
Aynan shu yerda Suspense'ni biladigan ma'lumotlarni olish kutubxonalari (React Query, SWR, Apollo Client, Relay) ajralmas bo'ladi. Ular Suspense'ni quyidagilarni ta'minlash orqali to'ldiradi:
- Mustahkam Keshlash: Ular olingan ma'lumotlarning murakkab xotira ichidagi keshini saqlab turadi, agar mavjud bo'lsa, uni darhol taqdim etadi va orqa fonda qayta tekshirishni boshqaradi.
- Ma'lumotlarni Bekor Qilish va Qayta Yuklash: Ular keshdagi ma'lumotlarni "eskirgan" deb belgilash va uni qayta yuklash mexanizmlarini taklif qiladi (masalan, mutatsiyadan, foydalanuvchi o'zaro ta'siridan so'ng yoki oynaga fokus qilinganda).
- Optimistik Yangilanishlar: Mutatsiyalar uchun ular sizga UI'ni darhol API so'rovining kutilgan natijasiga qarab (optimistik) yangilashga va agar haqiqiy API so'rovi muvaffaqiyatsiz bo'lsa, orqaga qaytarishga imkon beradi.
- Global Holat Sinxronizatsiyasi: Ular agar ma'lumotlar dasturning bir qismidan o'zgarsa, ushbu ma'lumotlarni ko'rsatadigan barcha komponentlar avtomatik ravishda yangilanishini ta'minlaydi.
- Mutatsiyalar uchun Yuklanish va Xato Holatlari:
useQuery
to'xtab qolishi mumkin bo'lsa-da,useMutation
odatda mutatsiya jarayonining o'zi uchunisLoading
vaisError
holatlarini taqdim etadi, chunki mutatsiyalar ko'pincha interaktiv bo'lib, darhol javob talab qiladi.
Mustahkam ma'lumotlarni olish kutubxonasisiz, bu xususiyatlarni qo'lda yozilgan Suspense resurs menejeri ustiga qurish jiddiy vazifa bo'lib, aslida sizdan o'z ma'lumotlarni olish freymvorkingizni yaratishni talab qiladi.
Amaliy Mulohazalar va Eng Yaxshi Amaliyotlar
Ma'lumotlarni olish uchun Suspense'ni qabul qilish muhim arxitekturaviy qarordir. Global dastur uchun ba'zi amaliy mulohazalar:
1. Barcha Ma'lumotlar Suspense'ga Muhtoj Emas
Suspense komponentning dastlabki renderiga bevosita ta'sir qiladigan muhim ma'lumotlar uchun idealdir. Muhim bo'lmagan ma'lumotlar, orqa fonda yuklashlar yoki kuchli vizual ta'sirsiz sekin yuklanishi mumkin bo'lgan ma'lumotlar uchun an'anaviy useEffect
yoki oldindan render qilish hali ham mos kelishi mumkin. Suspense'dan haddan tashqari foydalanish kamroq donador yuklanish tajribasiga olib kelishi mumkin, chunki bitta Suspense chegarasi *barcha* bolalari hal bo'lishini kutadi.
2. Suspense Chegaralarining Donadorligi
<Suspense>
chegaralaringizni o'ylab joylashtiring. Dasturning yuqori qismidagi bitta katta chegara butun sahifani spinner orqasiga yashirishi mumkin, bu esa asabiylashtirishi mumkin. Kichikroq, yanada donador chegaralar sahifangizning turli qismlarining mustaqil ravishda yuklanishiga imkon beradi, bu esa yanada progressiv va sezgir tajriba taqdim etadi. Masalan, foydalanuvchi profili komponenti atrofida bir chegara va tavsiya etilgan mahsulotlar ro'yxati atrofida yana bir chegara.
<div>
<h1>Mahsulot Sahifasi</h1>
<Suspense fallback={<p>Asosiy mahsulot tafsilotlari yuklanmoqda...</p>}>
<ProductDetails id="prod123" />
</Suspense>
<hr />
<h2>Tegishli Mahsulotlar</h2>
<Suspense fallback={<p>Tegishli mahsulotlar yuklanmoqda...</p>}>
<RelatedProducts category="electronics" />
</Suspense>
</div>
Bu yondashuv foydalanuvchilar tegishli mahsulotlar hali yuklanayotgan bo'lsa ham, asosiy mahsulot tafsilotlarini ko'ra olishlarini anglatadi.
3. Server Tomonida Render (SSR) va Oqimli HTML
React 18'ning yangi oqimli SSR API'lari (renderToPipeableStream
) Suspense bilan to'liq integratsiyalashgan. Bu serveringizga sahifaning qismlari (ma'lumotlarga bog'liq komponentlar kabi) hali yuklanayotgan bo'lsa ham, HTML tayyor bo'lishi bilan yuborishga imkon beradi. Server vaqtinchalik joy egallovchini (Suspense fallback'idan) oqim bilan yuborishi va keyin ma'lumotlar hal bo'lganda haqiqiy kontentni oqim bilan yuborishi mumkin, bu esa to'liq mijoz tomonida qayta render qilishni talab qilmaydi. Bu turli xil tarmoq sharoitlarida global foydalanuvchilar uchun seziladigan yuklanish unumdorligini sezilarli darajada yaxshilaydi.
4. Bosqichma-bosqich Qabul Qilish
Suspense'dan foydalanish uchun butun dasturingizni qayta yozishingiz shart emas. Uni bosqichma-bosqich, uning deklarativ yuklanish uslublaridan eng ko'p foyda ko'radigan yangi xususiyatlar yoki komponentlardan boshlab joriy qilishingiz mumkin.
5. Asboblar va Tuzatish (Debugging)
Suspense komponent mantig'ini soddalashtirsa-da, tuzatish boshqacha bo'lishi mumkin. React DevTools Suspense chegaralari va ularning holatlari haqida ma'lumot beradi. Tanlagan ma'lumotlarni olish kutubxonangiz o'zining ichki holatini qanday ko'rsatishi bilan tanishing (masalan, React Query Devtools).
6. Suspense Fallback'lari uchun Taymautlar
Juda uzoq yuklanish vaqtlari uchun siz Suspense fallback'ingizga taymaut kiritishingiz yoki ma'lum bir kechikishdan so'ng batafsilroq yuklanish ko'rsatkichiga o'tishingiz mumkin. React 18'dagi useDeferredValue
va useTransition
hooklari bu kabi nozik yuklanish holatlarini boshqarishga yordam beradi, bu sizga yangi ma'lumotlar olinayotganda UI'ning "eski" versiyasini ko'rsatishga yoki shoshilinch bo'lmagan yangilanishlarni kechiktirishga imkon beradi.
React'da Ma'lumotlarni Olish Kelajagi: React Server Komponentlari va Undan Keyingilari
React'da ma'lumotlarni olish sayohati mijoz tomonidagi Suspense bilan tugamaydi. React Server Komponentlari (RSC) muhim evolyutsiyani ifodalaydi, mijoz va server o'rtasidagi chegaralarni yo'qotishni va ma'lumotlarni olishni yanada optimallashtirishni va'da qiladi.
- React Server Komponentlari (RSC): Bu komponentlar serverda render bo'ladi, o'z ma'lumotlarini to'g'ridan-to'g'ri oladi va keyin faqat kerakli HTML va mijoz tomonidagi JavaScript'ni brauzerga yuboradi. Bu mijoz tomonidagi "sharshara"larni yo'q qiladi, paket hajmini kamaytiradi va dastlabki yuklanish unumdorligini yaxshilaydi. RSC'lar Suspense bilan birgalikda ishlaydi: server komponentlari agar ularning ma'lumotlari tayyor bo'lmasa to'xtab qolishi mumkin va server mijozga Suspense fallback'ini oqim bilan yuborishi mumkin, keyin esa ma'lumotlar hal bo'lganda almashtiriladi. Bu murakkab ma'lumotlar talablariga ega dasturlar uchun o'yinni o'zgartiruvchi omil bo'lib, ayniqsa turli geografik mintaqalardagi va o'zgaruvchan kechikishga ega foydalanuvchilar uchun muammosiz va yuqori unumdorlikli tajriba taklif etadi.
- Yagona Ma'lumotlarni Olish: React uchun uzoq muddatli istiqbol ma'lumotlarni olishga yagona yondashuvni o'z ichiga oladi, bunda asosiy freymvork yoki yaqindan integratsiyalashgan yechimlar ma'lumotlarni ham serverda, ham mijozda yuklash uchun birinchi darajali qo'llab-quvvatlashni ta'minlaydi, barchasi Suspense tomonidan boshqariladi.
- Kutubxona Evolyutsiyasining Davom Etishi: Ma'lumotlarni olish kutubxonalari rivojlanishda davom etadi, Suspense'ning asosiy imkoniyatlariga tayanib, keshlash, bekor qilish va real vaqtda yangilanishlar uchun yanada murakkab xususiyatlarni taklif qiladi.
React yetuklashib borgan sari, Suspense yuqori unumdor, foydalanuvchilar uchun qulay va qo'llab-quvvatlanishi oson dasturlar yaratish jumboqining tobora markaziy qismiga aylanadi. U dasturchilarni asenkron operatsiyalarni boshqarishning yanada deklarativ va barqaror usuliga undaydi, murakkablikni alohida komponentlardan yaxshi boshqariladigan ma'lumotlar qatlamiga o'tkazadi.
Xulosa
React Suspense, dastlab kod bo'lish uchun mo'ljallangan xususiyat bo'lib, ma'lumotlarni olish uchun o'zgartiruvchi vositaga aylandi. Render-qilish-bilan-birga-yuklash uslubini qabul qilish va Suspense'ni biladigan kutubxonalardan foydalanish orqali dasturchilar o'z dasturlarining foydalanuvchi tajribasini sezilarli darajada yaxshilashlari, yuklanish "sharshara"larini yo'q qilishlari, komponent mantig'ini soddalashtirishlari va silliq, muvofiqlashtirilgan yuklanish holatlarini ta'minlashlari mumkin. Mustahkam xatolarni boshqarish uchun Xatolik Chegaralari va React Server Komponentlarining kelajakdagi va'dasi bilan birgalikda, Suspense bizga nafaqat unumdor va barqaror, balki butun dunyodagi foydalanuvchilar uchun tabiiy ravishda yanada yoqimli bo'lgan dasturlar yaratish imkonini beradi. Suspense'ga asoslangan ma'lumotlarni olish paradigmasiga o'tish kontseptual moslashuvni talab qiladi, ammo kodning aniqligi, unumdorlik va foydalanuvchi mamnuniyati jihatidan afzalliklari sezilarli va sarmoya qilishga arziydi.