08 β Ro'yxatlar: FlatList va SectionList¶
β¬ οΈ Oldingi: 07 β ScrollView, SafeArea Β· π Kitob boshi Β· Keyingi: 09 β State va hodisalar β‘οΈ
Bu bobda: Uzun ro'yxatlarni (postlar, kontaktlar, mahsulotlar) qanday qilib tez va xotira-tejamkor ko'rsatishni o'rganamiz.
FlatListvirtualizatsiyasi nima,data/renderItem/keyExtractorqanday ishlaydi, bo'sh holat va separator qanday qo'shiladi, pull-to-refresh va cheksiz skroll qanday yoziladi,SectionListbilan bo'limli ro'yxat (alifbo bo'yicha kontaktlar) qanday quriladi va ro'yxatni tezlashtiradigan asosiy maslahatlar.
Muammo: 1000 ta elementni qanday ko'rsatamiz?¶
Tasavvur qiling, siz Instagram'ga o'xshash ilova yozyapsiz. Foydalanuvchi tasmasida (lentasida) minglab post bor. Yoki telefoningizdagi kontaktlar ilovasini eslang β u yerda yuzlab, ba'zan minglab ism saqlangan.
O'tgan bobda (07) biz ScrollView bilan tanishdik. Tabiiy fikr shu: "Hamma postni ScrollView ichiga joylashtiraman, foydalanuvchi skroll qiladi". Keling, oddiy misolni ko'ramiz:
// app/lenta.tsx β BU YOMON YONDASHUV (uzun ro'yxat uchun)
import { ScrollView, Text, View } from 'react-native';
const postlar = [/* ... 1000 ta post ... */];
export default function Lenta() {
return (
<ScrollView>
{postlar.map((post) => (
<View key={post.id}>
<Text>{post.sarlavha}</Text>
</View>
))}
</ScrollView>
);
}
Bu kod ishlaydi, lekin sekin. Nega? Chunki ScrollView ichidagi hamma elementni β ekranga sig'maydigan, foydalanuvchi hech qachon ko'rmasligi mumkin bo'lganlarini ham β darhol chizadi (render qiladi). 1000 ta <View> va <Text> bir vaqtda yaratiladi:
- Ilova ochilishi sekinlashadi (1000 ta element tayyorlanguncha kutasiz).
- Xotira to'ladi (1000 ta UI element xotirada turadi).
- Skroll sakraydi, lag beradi.
Hayotiy o'xshatish. Tasavvur qiling, kutubxonachidan bitta kitob so'radingiz. U sizga 1000 ta kitobni bittadan emas, balki hammasini birdan stolingizga tashlasa β stol to'lib ketadi, kerakli kitobni topish qiyinlashadi. Aqlli kutubxonachi esa faqat sizga hozir kerak bo'lganini beradi, qolganini javonda saqlaydi.
FlatListβ aynan shunday aqlli kutubxonachi.
Yechim: FlatList va virtualizatsiya¶
FlatList β bu uzun ro'yxatlar uchun maxsus komponent. U virtualizatsiya degan usulni qo'llaydi: faqat ekranda hozir ko'rinadigan (va biroz atrofdagi) elementlarni render qiladi. Foydalanuvchi pastga skroll qilsa, tepaga chiqib ketgan elementlar xotiradan tushadi, yangi ko'rinadiganlari render bo'ladi.
Hayotiy o'xshatish.
FlatListβ zavoddagi konveyer lenta kabi. Ishchi (ekran) faqat ko'z oldidagi bir nechta buyumni ko'radi va ishlaydi. Lenta aylanadi: yangi buyumlar keladi, ishlangani ketadi. Hamma 1000 buyum bir vaqtda ishchining oldida turmaydi β faqat hozir kerak bo'lgani.
Qachon qaysi?
Kam element (taxminan 10-20 tagacha, oldindan ma'lum, qisqa) β ScrollView yetarli va soddaroq (masalan, sozlamalar sahifasi). Uzun yoki noma'lum uzunlikdagi ro'yxat (postlar, kontaktlar, mahsulotlar) β har doim FlatList (yoki SectionList). Shubha bo'lsa, FlatList ni tanlang.
FlatList asoslari: uchta asosiy prop¶
FlatList ishlashi uchun unga kamida ikkita narsa kerak: qaysi ma'lumotni ko'rsatish (data) va har bir element qanday ko'rinishi (renderItem). Uchinchisi β keyExtractor β texnik jihatdan ixtiyoriy, lekin amalda deyarli har doim kerak. Keling, har birini ko'rib chiqamiz.
1. data β ko'rsatiladigan ma'lumotlar¶
data β bu massiv. Har bir element ro'yxatdagi bitta qator (yoki karta) bo'ladi. Odatda bu obyektlar massivi:
2. renderItem β har bir element qanday ko'rinadi¶
renderItem β bu funksiya. FlatList uni har bir element uchun chaqiradi va sizga ma'lumotni argument sifatida beradi. Argument β obyekt, uning ichida item xususiyati bor (bu β data massividagi joriy element):
Bu yerda { item } β destrukturizatsiya (obyektdan item ni ajratib olish). FlatList aslida funksiyaga { item, index, separators } beradi; bizga ko'pincha faqat item kerak.
Eslatma. React Native qoidasini unutmang: matn har doim
<Text>ichida bo'lishi shart.renderItemichida to'g'ridan-to'g'ri{item.nomi}yozsangiz (View yoki Text'siz), ilova qulaydi. Bu boshlovchilarning eng keng tarqalgan xatosi.
3. keyExtractor β har element uchun noyob kalit¶
keyExtractor β bu har element uchun noyob (takrorlanmas) identifikator (kalit, ya'ni key) qaytaradigan funksiya. U string qaytarishi kerak:
Nega bu muhim? React har bir elementni o'ziga xos "tug'ilish guvohnomasi" bilan tanib oladi. Kalit yordamida React qaysi element o'zgardi, qaysi biri qo'shildi yoki o'chirildi β buni aniq biladi. Kalitsiz React har safar hammasini qaytadan chizishga majbur bo'ladi yoki noto'g'ri elementni yangilaydi (masalan, ro'yxat o'rtasidan element o'chirsangiz, noto'g'ri qator g'oyib bo'ladi).
Indeksni kalit qilmang
Vasvasaga tushib keyExtractor={(item, index) => index.toString()} deb yozmang. Indeks β elementning o'rni, uning o'ziligi emas. Ro'yxat tartibi o'zgarsa (saralash, element qo'shish/o'chirish), indekslar siljiydi va React noto'g'ri elementlarni chalkashtiradi. Har doim ma'lumotning barqaror, noyob maydonidan (odatda id) foydalaning.
Birinchi to'liq FlatList¶
Mana uch propni birlashtirgan eng oddiy, ishlaydigan ro'yxat:
// app/xaridlar.tsx
import { FlatList, Text, View, StyleSheet } from 'react-native';
const xaridlar = [
{ id: 1, nomi: 'Non' },
{ id: 2, nomi: 'Sut' },
{ id: 3, nomi: 'Tuxum' },
];
export default function Xaridlar() {
return (
<FlatList
data={xaridlar}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => (
<View style={styles.qator}>
<Text style={styles.matn}>{item.nomi}</Text>
</View>
)}
/>
);
}
const styles = StyleSheet.create({
qator: { padding: 16, backgroundColor: '#ffffff' },
matn: { fontSize: 16, color: '#1e293b' },
});
Eslatma (SDK 56). Bu kitob misollarida qisqalik uchun
app/yo'lini ishlatamiz. Sizning loyihangiz SDK 56 shabloni bo'lsa, fayllarsrc/app/ichida bo'ladi β qoidalar aynan bir xil, faqat papka nomi farq qiladi.Eslatma.
FlatListo'zi skroll bo'ladi β uniScrollViewichiga solmang. Aks hola virtualizatsiya buziladi va ogohlantirish (warning) chiqadi.FlatListallaqachon "skroll qiluvchi konteyner".
Foydali proplar: header, footer, bo'sh holat, separator¶
Haqiqiy ilovada ro'yxat shunchaki qatorlardan iborat emas. Yuqorida sarlavha, qatorlar orasida ajratuvchi chiziq, ro'yxat bo'sh bo'lsa esa do'stona xabar kerak bo'ladi. FlatList bularning hammasi uchun tayyor proplar beradi.
ListHeaderComponent va ListFooterComponent¶
Ro'yxatning boshiga (header) yoki oxiriga (footer) qo'shiladigan komponent. Header β ko'pincha sarlavha yoki qidiruv maydoni; footer β "Ko'proq yuklash" tugmasi yoki yuklanish indikatori:
<FlatList
data={postlar}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => <PostQatori post={item} />}
ListHeaderComponent={<Text style={styles.sarlavha}>So'nggi postlar</Text>}
ListFooterComponent={<Text style={styles.footer}>Tugadi π</Text>}
/>
Header β ro'yxat bilan birga skroll bo'ladi
ListHeaderComponent ro'yxat ustida turadi va u bilan birga yuqoriga skroll bo'lib ketadi. Agar sarlavhani doimo ekran tepasida (qotirilgan) ushlab turmoqchi bo'lsangiz, uni FlatListdan tashqarida, alohida <View> ichiga qo'ying.
ItemSeparatorComponent β elementlar orasidagi ajratgich¶
Bu komponent har ikki element orasiga qo'yiladi (birinchidan oldin va oxirgidan keyin emas β aynan oralarda). Ko'pincha nozik chiziq yoki bo'shliq:
function Ajratgich() {
return <View style={{ height: 1, backgroundColor: '#e2e8f0' }} />;
}
<FlatList
data={postlar}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => <PostQatori post={item} />}
ItemSeparatorComponent={Ajratgich}
/>
Komponent uzatishning ikki usuli
ItemSeparatorComponent ga komponentni funksiya sifatida ({Ajratgich} β JSX'siz) berasiz, ListHeaderComponent ga esa odatda JSX element ({<Text>...</Text>}) yoki funksiya berishingiz mumkin. Ikkalasi ham ishlaydi; misollarda har ikkala uslubni ko'rasiz.
ListEmptyComponent β bo'sh holat¶
data massivi bo'sh ([]) bo'lganda ko'rsatiladigan komponent. Bu juda muhim: bo'sh ekran foydalanuvchini chalg'itadi ("ilova buzilganmi?"). Yaxshi ilova bo'sh holatni do'stona tushuntiradi:
function BoshHolat() {
return (
<View style={styles.bosh}>
<Text style={styles.boshMatn}>Hozircha post yo'q</Text>
<Text style={styles.boshKichik}>Birinchi postni siz yozing!</Text>
</View>
);
}
<FlatList
data={postlar}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => <PostQatori post={item} />}
ListEmptyComponent={BoshHolat}
/>
numColumns β grid (panjara) ko'rinishi¶
Ro'yxatni bir ustun emas, bir necha ustun (grid) qilib ko'rsatish uchun numColumns ni bering. Bu fotogalereya yoki mahsulot panjarasi uchun ideal:
<FlatList
data={rasmlar}
numColumns={2}
keyExtractor={(item) => item.id.toString()}
columnWrapperStyle={{ gap: 12 }}
renderItem={({ item }) => (
<View style={styles.katak}>
<Text>{item.nomi}</Text>
</View>
)}
/>
columnWrapperStyle β bir qatordagi ustunlar orasidagi masofa va tekislash uchun. Element stilida flex: 1 ishlatsangiz, ustunlar ekran kengligini teng bo'lishadi.
numColumns o'zgartirilsa
numColumns ni dinamik o'zgartirmoqchi bo'lsangiz (masalan, ekran burilganda 2 dan 3 ga), FlatList ga key prop berib uni qaytadan yaratish kerak bo'ladi. Aks holda ogohlantirish chiqadi.
horizontal β yotiq ro'yxat¶
horizontal propi ro'yxatni yon tomonga (yotiq) skroll qiladigan qiladi β "stories" yoki "tavsiya etilgan mahsulotlar" lentasi kabi:
<FlatList
data={tavsiyalar}
horizontal
showsHorizontalScrollIndicator={false}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => <TavsiyaKarta mahsulot={item} />}
/>
Pull-to-refresh: pastga torting va yangilang¶
Mobil ilovalarda juda tanish harakat: ro'yxat tepasida turib pastga torting β ro'yxat yangilanadi (yangi postlar yuklanadi). Buni "pull-to-refresh" deyiladi.
Hayotiy o'xshatish. Bu β oynani pastga tushirib, yangi havodan nafas olishga o'xshaydi. Foydalanuvchi "menga eng so'nggi ma'lumotni ber" deydi, ilova esa serverdan yangilarini olib keladi.
FlatList da buni ikki prop bilan qilamiz:
refreshingβ boolean.truebo'lsa, aylanuvchi indikator (spinner) ko'rinadi.onRefreshβ funksiya. Foydalanuvchi pastga tortganda chaqiriladi; bu yerda ma'lumotni yangilaysiz.
// app/lenta.tsx
import { useState, useCallback } from 'react';
import { FlatList, Text, View, StyleSheet } from 'react-native';
type Post = { id: number; sarlavha: string };
export default function Lenta() {
const [postlar, setPostlar] = useState<Post[]>([]);
const [yangilanmoqda, setYangilanmoqda] = useState(false);
const yangilash = useCallback(async () => {
setYangilanmoqda(true); // spinnerni yoqamiz
// haqiqiy ilovada bu yerda serverdan ma'lumot olamiz (17-bob)
const yangi = await soxtaYukla();
setPostlar(yangi);
setYangilanmoqda(false); // spinnerni o'chiramiz
}, []);
return (
<FlatList
data={postlar}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => (
<View style={styles.qator}>
<Text>{item.sarlavha}</Text>
</View>
)}
refreshing={yangilanmoqda}
onRefresh={yangilash}
/>
);
}
// namuna: 1 soniyada soxta ma'lumot qaytaradi
function soxtaYukla(): Promise<Post[]> {
return new Promise((resolve) =>
setTimeout(
() => resolve([
{ id: 1, sarlavha: 'Yangilangan post 1' },
{ id: 2, sarlavha: 'Yangilangan post 2' },
]),
1000,
),
);
}
const styles = StyleSheet.create({
qator: { padding: 16, borderBottomWidth: 1, borderBottomColor: '#e2e8f0' },
});
Tarmoq keyinroq
Bu yerda serverdan haqiqiy ma'lumot olishni soxtaYukla bilan taqlid qildik. Haqiqiy fetch/API bilan ishlashni 17-bobda (Tarmoq va API) batafsil o'rganamiz.
Cheksiz skroll (infinite scroll): pastga yetganda ko'proq yukla¶
Ijtimoiy tarmoqlarda postlar "tugamaydi" β pastga skroll qilaverasiz, yangi postlar yuklanaveradi. Bu β cheksiz skroll (yoki "lazy loading", ya'ni asta-sekin yuklash). Bir vaqtda hamma postni yuklash o'rniga, ro'yxat oxiriga yaqinlashganda keyingi "sahifani" yuklaymiz.
FlatList da ikkita prop:
onEndReachedβ funksiya. Foydalanuvchi ro'yxat oxiriga yaqinlashganda chaqiriladi; bu yerda keyingi qismni yuklaysiz.onEndReachedThresholdβ raqam (0 dan 1 gacha). Oxirgacha qancha qolgandaonEndReachedishga tushishini belgilaydi.0.5β ro'yxat oxiriga yarim ekran qolganda.0.1β deyarli tagiga yetganda.
const [postlar, setPostlar] = useState<Post[]>(boshlangichPostlar);
const [koproqYuklanmoqda, setKoproqYuklanmoqda] = useState(false);
const koproqYukla = useCallback(async () => {
if (koproqYuklanmoqda) return; // ikki marta yuklamaslik
setKoproqYuklanmoqda(true);
const keyingiSahifa = await sahifaYukla(postlar.length);
setPostlar((avval) => [...avval, ...keyingiSahifa]); // qo'shamiz
setKoproqYuklanmoqda(false);
}, [postlar.length, koproqYuklanmoqda]);
<FlatList
data={postlar}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => <PostQatori post={item} />}
onEndReached={koproqYukla}
onEndReachedThreshold={0.5}
ListFooterComponent={
koproqYuklanmoqda ? <ActivityIndicator /> : null
}
/>
E'tibor bering: yangi postlarni qo'shamiz ([...avval, ...keyingiSahifa]), almashtirmaymiz. Eski postlar joyida qoladi, tagiga yangilari ulanadi.
Ikki marta chaqirilishidan ehtiyot bo'ling
onEndReached ba'zan bir necha marta ketma-ket chaqirilishi mumkin. Yuqoridagi if (koproqYuklanmoqda) return; qatori β bir vaqtda faqat bitta yuklash ketishini ta'minlovchi oddiy "qulf". Busiz bir xil postlar ikki marta yuklanib qolishi mumkin.
SectionList: bo'limli ro'yxat¶
Ba'zan ro'yxatni guruhlarga ajratish kerak. Klassik misol β telefon kontaktlari: ismlar alifbo bo'yicha bo'limlarga ajraladi (A bo'limi, B bo'limi, D bo'limi...), har bo'lim ustida harf-sarlavha turadi. Yana misollar: kunlar bo'yicha guruhlangan vazifalar, kategoriyalar bo'yicha mahsulotlar, sanalar bo'yicha xabarlar.
Buning uchun FlatList emas, SectionList ishlatiladi. U ham virtualizatsiyalangan (tez), faqat ma'lumot tuzilishi va proplari biroz boshqacha.
sections β bo'limlar massivi¶
FlatList da data oddiy massiv edi. SectionList da esa sections β bu bo'limlar massivi. Har bir bo'lim β obyekt, uning ichida ikkita maydon:
titleβ bo'lim sarlavhasi (masalan, harf "A").dataβ shu bo'limdagi elementlar massivi.
const bolimlar = [
{ title: 'A', data: [{ id: 'a1', ism: 'Aziz' }, { id: 'a2', ism: 'Anvar' }] },
{ title: 'B', data: [{ id: 'b1', ism: 'Bobur' }] },
{ title: 'D', data: [{ id: 'd1', ism: 'Dilnoza' }, { id: 'd2', ism: 'Davron' }] },
];
renderItem va renderSectionHeader¶
SectionList ikkita render funksiyasini kutadi:
renderItem={({ item }) => ...}β har bir element (kontakt) qanday ko'rinadi (FlatListdagi kabi).renderSectionHeader={({ section }) => ...}β har bir bo'lim sarlavhasi qanday ko'rinadi. Bu yerdasectionβ joriy bo'lim obyekti, undansection.titleni olasiz.
// app/kontaktlar.tsx
import { SectionList, Text, View, StyleSheet } from 'react-native';
type Kontakt = { id: string; ism: string };
const bolimlar = [
{ title: 'A', data: [{ id: 'a1', ism: 'Aziz' }, { id: 'a2', ism: 'Anvar' }] },
{ title: 'B', data: [{ id: 'b1', ism: 'Bobur' }] },
{ title: 'D', data: [{ id: 'd1', ism: 'Dilnoza' }, { id: 'd2', ism: 'Davron' }] },
];
export default function Kontaktlar() {
return (
<SectionList
sections={bolimlar}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<Text style={styles.kontakt}>{item.ism}</Text>
)}
renderSectionHeader={({ section }) => (
<Text style={styles.bolimSarlavha}>{section.title}</Text>
)}
/>
);
}
const styles = StyleSheet.create({
kontakt: { fontSize: 16, color: '#1e293b', paddingVertical: 10, paddingHorizontal: 16 },
bolimSarlavha: {
fontSize: 14,
fontWeight: '700',
color: '#0284c7',
backgroundColor: '#e0f2fe',
paddingVertical: 6,
paddingHorizontal: 16,
},
});
Sticky (yopishqoq) sarlavhalar
SectionList da bo'lim sarlavhalari sukut bo'yicha (default) yopishqoq β siz skroll qilganda joriy bo'lim sarlavhasi ekran tepasida turib qoladi, keyingi bo'lim kelguncha. Buni o'chirmoqchi bo'lsangiz: stickySectionHeadersEnabled={false}.
Eslatma.
SectionListhamFlatListning ko'p proplarini qo'llaydi:ItemSeparatorComponent,ListEmptyComponent,ListHeaderComponent,refreshing/onRefresh,onEndReached. Ya'ni biz yuqorida o'rganganlarning aksariyati bu yerda ham ishlaydi.
Element komponentini ajratish (qayta ishlatish + memo)¶
Hozirgacha renderItem ichiga JSX'ni to'g'ridan-to'g'ri yozdik. Ro'yxat elementi murakkablashganda (rasm, sarlavha, matn, tugmalar) bu funksiya katta va chalkash bo'lib ketadi. Yaxshi yechim β har bir elementni alohida komponentga ajratish:
// components/PostKarta.tsx
import { View, Text, Pressable, StyleSheet } from 'react-native';
type Post = { id: number; sarlavha: string; muallif: string };
type Props = {
post: Post;
onPress: (id: number) => void;
};
function PostKarta({ post, onPress }: Props) {
return (
<Pressable style={styles.karta} onPress={() => onPress(post.id)}>
<Text style={styles.sarlavha}>{post.sarlavha}</Text>
<Text style={styles.muallif}>Muallif: {post.muallif}</Text>
</Pressable>
);
}
const styles = StyleSheet.create({
karta: { backgroundColor: '#ffffff', borderRadius: 12, padding: 16 },
sarlavha: { fontSize: 16, fontWeight: '700', color: '#1e293b' },
muallif: { fontSize: 13, color: '#475569', marginTop: 4 },
});
export default PostKarta;
Bunday ajratishning ikkita foydasi bor:
- Qayta ishlatish β bir xil karta boshqa ekranlarda ham kerak bo'lsa, tayyor.
- Tezlik (memoizatsiya) β komponentni
React.memobilan o'rab, keraksiz qayta render'larning oldini olamiz.
React.memo nima qiladi?¶
React.memo β bu komponentni "o'rab" beradigan funksiya. U komponentga props o'zgarmasa, qayta render qilma deydi. Ro'yxatlarda bu juda muhim: bitta element o'zgarganda, qolgan yuzlab elementlar behuda qaytadan chizilmasligi kerak.
import { memo } from 'react';
const PostKarta = memo(function PostKarta({ post, onPress }: Props) {
// ... yuqoridagi kabi
});
Endi post va onPress o'zgarmasa, bu karta qayta render bo'lmaydi.
memo va funksiya proplari
React.memo faqat props bir xil bo'lsa ishlaydi. Lekin renderItem ichida har render'da yangi funksiya yaratsangiz (masalan, onPress={() => ...}), onPress har safar "yangi" hisoblanadi va memo foydasiz bo'ladi. Yechim: funksiyani useCallback bilan o'rab, barqaror qiling. Buni quyidagi to'liq misolda ko'rasiz.
To'liq misol: postlar ro'yxati¶
Endi hamma narsani birlashtiramiz: ajratilgan + memoizatsiya qilingan element komponenti, separator, header, bo'sh holat, pull-to-refresh va useCallback bilan barqaror funksiyalar. Bu kod jonli Expo loyihada tsc bilan tip-tekshiruvidan o'tgan.
// app/postlar.tsx
import { useState, useCallback, memo } from 'react';
import {
FlatList,
View,
Text,
Pressable,
StyleSheet,
type ListRenderItemInfo,
} from 'react-native';
type Post = { id: number; sarlavha: string; muallif: string };
const BOSHLANGICH: Post[] = [
{ id: 1, sarlavha: 'React Native nima?', muallif: 'Oqil' },
{ id: 2, sarlavha: 'FlatList sirlari', muallif: 'Dilnoza' },
{ id: 3, sarlavha: 'Expo bilan tezkor start', muallif: 'Sardor' },
];
// ββ Element komponenti (ajratilgan + memo) ββ
type KartaProps = { post: Post; onPress: (id: number) => void };
const PostKarta = memo(function PostKarta({ post, onPress }: KartaProps) {
return (
<Pressable style={styles.karta} onPress={() => onPress(post.id)}>
<Text style={styles.kartaSarlavha}>{post.sarlavha}</Text>
<Text style={styles.kartaMuallif}>Muallif: {post.muallif}</Text>
</Pressable>
);
});
// ββ Yordamchi komponentlar ββ
function Ajratgich() {
return <View style={styles.ajratgich} />;
}
function BoshHolat() {
return (
<View style={styles.bosh}>
<Text style={styles.boshMatn}>Hozircha post yo'q</Text>
</View>
);
}
// ββ Asosiy ekran ββ
export default function Postlar() {
const [postlar, setPostlar] = useState<Post[]>(BOSHLANGICH);
const [yangilanmoqda, setYangilanmoqda] = useState(false);
// pull-to-refresh
const yangilash = useCallback(() => {
setYangilanmoqda(true);
setTimeout(() => {
setPostlar(BOSHLANGICH);
setYangilanmoqda(false);
}, 1000);
}, []);
// element bosilganda β barqaror funksiya (memo ishlashi uchun)
const ochish = useCallback((id: number) => {
console.log('Post bosildi:', id);
}, []);
// renderItem β useCallback bilan barqaror
const renderItem = useCallback(
({ item }: ListRenderItemInfo<Post>) => (
<PostKarta post={item} onPress={ochish} />
),
[ochish],
);
return (
<FlatList
data={postlar}
keyExtractor={(item) => item.id.toString()}
renderItem={renderItem}
ItemSeparatorComponent={Ajratgich}
ListHeaderComponent={<Text style={styles.sarlavha}>So'nggi postlar</Text>}
ListEmptyComponent={BoshHolat}
refreshing={yangilanmoqda}
onRefresh={yangilash}
contentContainerStyle={styles.konteyner}
/>
);
}
const styles = StyleSheet.create({
konteyner: { padding: 16 },
sarlavha: { fontSize: 22, fontWeight: '700', color: '#1e293b', marginBottom: 16 },
karta: { backgroundColor: '#ffffff', borderRadius: 12, padding: 16 },
kartaSarlavha: { fontSize: 16, fontWeight: '700', color: '#1e293b' },
kartaMuallif: { fontSize: 13, color: '#475569', marginTop: 4 },
ajratgich: { height: 12 },
bosh: { padding: 40, alignItems: 'center' },
boshMatn: { fontSize: 16, color: '#94a3b8' },
});
Bu misolda diqqat qiling:
ListRenderItemInfo<Post>βrenderItemargumentining tipi (react-nativedan). TypeScript yordamidaitemning tipini aniq belgilaydi.contentContainerStyleβ bu ro'yxatning ichki stili (padding shu yerda).styleesa konteynerning tashqi o'lchamiga ta'sir qiladi. Padding kerak bo'lsa,contentContainerStyleishlating.ochishvarenderItemuseCallbackbilan o'ralgan β shuning uchunPostKartaningmemosi haqiqatan ishlaydi.
Performance maslahatlari¶
FlatList o'zi tez, lekin uzun ro'yxatlarni yanada silliq qilish uchun bir nechta amal bor. Bularni endi qisqacha tanishtirib o'tamiz; chuqur 27-bobda (Performance) qaytamiz.
Asosiy maslahatlar
keyExtractorni har doim bering β indeks emas, barqarorid. Bu eng muhim qoida.- Element komponentini ajrating va
React.memoqiling β keraksiz qayta render'lar kamayadi. renderItemva element'ga uzatiladigan funksiyalarniuseCallbackqiling β memo ishlashi uchun.getItemLayoutβ agar har bir element bir xil balandlikda bo'lsa,FlatListga balandlikni oldindan ayting. Shunda u har birini o'lchashga vaqt sarflamaydi va skroll tezroq bo'ladi.initialNumToRenderβ birinchi marta nechta element render qilinishi. Birinchi ekran sig'imicha qiymat (masalan, 10) β ortig'ini render qilmaslik ilova ochilishini tezlashtiradi.
getItemLayout ko'rinishi (har element 64px balandlikda deylik):
const BALANDLIK = 64;
<FlatList
data={postlar}
keyExtractor={(item) => item.id.toString()}
renderItem={renderItem}
getItemLayout={(_, index) => ({
length: BALANDLIK, // har element balandligi
offset: BALANDLIK * index, // shu elementgacha bo'lgan masofa
index,
})}
/>
getItemLayout faqat bir xil balandlik uchun
getItemLayout ni faqat har bir element balandligi aniq bir xil bo'lsa ishlating. Agar elementlar turli balandlikda bo'lsa (masalan, har xil uzunlikdagi matn), getItemLayout noto'g'ri hisoblaydi va skroll buziladi.
Xulosa¶
- Uzun ro'yxatlar uchun
ScrollViewemas,FlatListishlating.ScrollViewhamma elementni darhol render qiladi (sekin, xotira to'ladi);FlatListfaqat ekranda ko'rinadiganlarni render qiladi (virtualizatsiya β "konveyer lenta"). FlatListning uchta asosi:data(massiv),renderItem(({ item }) => ...),keyExtractor((item) => item.id.toString()β barqaror, noyob kalit; indeks emas!).- Matn har doim
<Text>ichida βrenderItemichida ham bu qoida amal qiladi. - Foydali proplar:
ListEmptyComponent(bo'sh holat),ListHeaderComponent/ListFooterComponent,ItemSeparatorComponent,numColumns(grid),horizontal(yotiq). - Pull-to-refresh:
refreshing={boolean}+onRefresh={funksiya}. Cheksiz skroll:onEndReached+onEndReachedThreshold(yangi elementlarni qo'shing, almashtirmang). SectionListβ bo'limli ro'yxat (alifbo bo'yicha kontaktlar):sections({ title, data }massivi) +renderSectionHeader. Sarlavhalar default yopishqoq (sticky).- Tezlik uchun: element komponentini ajrating +
React.memo, funksiyalarniuseCallback, bir xil balandlikdagetItemLayout,initialNumToRender. Chuqurroq β 27-bobda.
Amaliy mashqlar¶
-
Xaridlar ro'yxati (oson).
FlatListbilan 5-6 mahsulotdan iborat xaridlar ro'yxati yarating. Har element β<View>ichida mahsulot nomi va narxi.keyExtractorvaItemSeparatorComponent(nozik chiziq) qo'shing. Ro'yxat bo'sh bo'lganda "Savatcha bo'sh" deganListEmptyComponentko'rsating (datani vaqtincha[]qilib sinab ko'ring). -
Postlar va pull-to-refresh (o'rta). Postlar ro'yxatini tuzing. Element komponentini alohida fayl/komponentga ajrating va
React.memobilan o'rang. Pastga tortilganda (refreshing+onRefresh) ro'yxat boshiga yangi post qo'shilsin (masalan,setTimeoutbilan 1 soniyada).useCallbackto'g'ri ishlatilganini tekshiring. -
Alifbo bo'yicha kontaktlar (o'rta).
SectionListbilan kontaktlar ilovasi yarating: kamida 3 ta bo'lim (A, B, D), har bo'limda 2-3 ismdan.renderSectionHeaderda harfni ko'k fon bilan ko'rsating. Keyin: oddiy ismlar massividan (bo'limsiz) bo'limlarni avtomatik hosil qiluvchi funksiya yozing (har ismning birinchi harfi bo'yicha guruhlang). -
Grid galereya (qiyin).
numColumns={2}bilan rang kartalari panjarasini yarating (har element β rangli kvadrat va uning nomi).columnWrapperStylebilan ustunlar orasiga masofa qo'ying, element stilidaflex: 1ishlatib ustunlarni tenglashtiring. Bonus: ekran kengligi (useWindowDimensions, 05-bobdan) 600px dan katta bo'lsa,numColumnsni 3 ga o'zgartiring (FlatListgakeyberishni unutmang). -
Cheksiz skroll (qiyin). Postlar ro'yxatiga
onEndReachedqo'shing: tagiga yetganda 10 ta yangi post qo'shilsin (setTimeoutbilan soxta yuklash). Bir vaqtda faqat bitta yuklash ketishini ta'minlovchi "qulf" (if (yuklanmoqda) return) yozing vaListFooterComponentda yuklanish indikatori (ActivityIndicator) ko'rsating.
β¬ οΈ Oldingi: 07 β ScrollView, SafeArea Β· π Kitob boshi Β· Keyingi: 09 β State va hodisalar β‘οΈ