Explore all courses in this category.
Explore all courses in this category.
Nguyễn Đình Cường
0 comment
09 Nov, 2026
Danh sách là thành phần xuất hiện ở hầu hết ứng dụng: bảng tin, sản phẩm, tin nhắn. Nếu làm không khéo, danh sách dài khiến ứng dụng giật và tốn bộ nhớ. FlatList của React Native được thiết kế để xử lý việc này, nhưng bạn cần dùng đúng cách.
ScrollView vẽ tất cả phần tử con ngay lập tức, kể cả những mục nằm ngoài màn hình. Với vài chục mục thì không sao, nhưng với hàng trăm hoặc hàng nghìn mục, bộ nhớ và thời gian khởi tạo tăng mạnh. FlatList chỉ vẽ các mục đang nằm trong và gần vùng nhìn thấy, đồng thời tái sử dụng tài nguyên khi cuộn.
import { FlatList, Text, View } from 'react-native';
type Item = { id: string; title: string };
export default function ProductList({ data }: { data: Item[] }) {
return (
<FlatList
data={data}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={{ padding: 16 }}>
<Text>{item.title}</Text>
</View>
)}
ItemSeparatorComponent={() => <View style={{ height: 1, backgroundColor: '#eee' }} />}
ListEmptyComponent={<Text>Chưa có dữ liệu</Text>}
/>
);
}
Ba thuộc tính bắt buộc về mặt thực tế là data, renderItem và keyExtractor. Khóa phải duy nhất và ổn định; tránh dùng chỉ số mảng nếu danh sách có thể đổi thứ tự hoặc thêm xóa.
const Row = React.memo(function Row({ item }: { item: Item }) {
return (
<View style={{ height: 64, justifyContent: 'center', paddingHorizontal: 16 }}>
<Text>{item.title}</Text>
</View>
);
});
const renderItem = useCallback(({ item }: { item: Item }) => <Row item={item} />, []);
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(i) => i.id}
getItemLayout={(_, index) => ({ length: 64, offset: 64 * index, index })}
/>
FlatList hỗ trợ sẵn hai mẫu quen thuộc. Kéo xuống để làm mới dùng refreshing và onRefresh. Tải thêm khi cuộn gần cuối dùng onEndReached với onEndReachedThreshold.
<FlatList
data={items}
renderItem={renderItem}
keyExtractor={(i) => i.id}
refreshing={refreshing}
onRefresh={reload}
onEndReached={loadMore}
onEndReachedThreshold={0.5}
ListFooterComponent={loading ? <ActivityIndicator /> : null}
/>
Hãy chặn việc gọi loadMore chồng chéo bằng cờ loading, nếu không một lần cuộn có thể kích hoạt nhiều yêu cầu cùng lúc.
Có nên dùng SectionList không? Có, khi dữ liệu được chia nhóm với tiêu đề từng nhóm, ví dụ danh bạ theo chữ cái.
Có thư viện nào nhanh hơn không? Có các thư viện danh sách tối ưu hóa của cộng đồng; hãy đo hiệu năng thực tế trước khi chuyển.
Chìa khóa của danh sách mượt là chỉ vẽ cái cần vẽ và tránh vẽ lại không cần thiết. Hãy đo bằng công cụ phân tích hiệu năng trên thiết bị thật thay vì đoán, vì thiết bị cấu hình thấp mới là nơi vấn đề lộ ra.
Trước khi tối ưu, hãy tái hiện vấn đề trên một điện thoại cấu hình thấp ở chế độ release, vì chế độ phát triển luôn chậm hơn thực tế. Quan sát hai chỉ số: số khung hình mỗi giây của luồng giao diện và của luồng JavaScript. Nếu luồng JavaScript tụt khi cuộn, nguyên nhân thường là renderItem làm việc nặng hoặc state thay đổi quá thường xuyên khiến nhiều mục bị vẽ lại.
Một mẹo hữu ích là giữ cho dữ liệu mỗi mục nhỏ gọn và đã được chuẩn bị sẵn (ví dụ chuỗi đã định dạng) thay vì tính toán lúc vẽ. Với danh sách có ảnh, hãy dùng ảnh đã thu nhỏ từ máy chủ. Cuối cùng, thử đặt removeClippedSubviews trên Android và so sánh trước sau; không phải tùy chọn nào cũng có lợi trong mọi trường hợp, nên mọi thay đổi cần được đo lại.
Để luyện cách xây các màn hình danh sách thực tế, bạn có thể xem thêm bài học tại codebem.com.
Nguyễn Đình Cường
0 comment