Giới thiệu
Trong các ứng dụng di động sử dụng React Native, danh sách dài thường được render bằng thành phần FlatList. Khi dữ liệu lên tới hàng nghìn mục, nếu không tối ưu, FlatList có thể gây giật, chậm phản hồi và tiêu tốn tài nguyên. Bài viết này sẽ phân tích các nguyên nhân gây giảm hiệu suất và cung cấp các kỹ thuật thực tiễn để tối ưu, đồng thời minh họa bằng mã TypeScript sạch.
Nguyên nhân thường gặp khiến FlatList chậm
1. Render lại toàn bộ danh sách
Mỗi khi state hoặc props thay đổi, hàm renderItem mặc định sẽ được gọi lại cho tất cả các mục, ngay cả khi chúng không thay đổi. Điều này làm tăng số lần render không cần thiết.
2. Thiếu keyExtractor ổn định
Nếu key không ổn định, React sẽ phải tái tạo lại các phần tử DOM ảo, gây tốn kém.
3. Không cung cấp getItemLayout
Không biết trước chiều cao của mỗi mục khiến FlatList phải đo từng phần tử trong quá trình scroll, làm giảm tốc độ.
Kỹ thuật tối ưu hiệu suất
1. Memo hoá hàm render và key extractor
Sử dụng useCallback để giữ nguyên tham chiếu hàm giữa các lần render. Khi hàm không thay đổi, FlatList sẽ không gọi lại chúng.
2. Sử dụng React.memo cho component mục
Đóng gói component hiển thị mỗi mục bằng React.memo giúp React bỏ qua render lại khi props không thay đổi.
3. Cấu hình FlatList hợp lý
- initialNumToRender: số mục render ban đầu, giảm tải khi khởi tạo.
- maxToRenderPerBatch: giới hạn số mục render trong mỗi batch.
- windowSize: số lần viewport được nhân lên để giữ lại các mục đã render.
4. Cung cấp getItemLayout
Nếu chiều cao mục cố định, trả về length, offset và index giúp FlatList tính toán vị trí nhanh hơn.
Một ví dụ thực tế
Dưới đây là một component OptimizedFlatList hoàn chỉnh, sử dụng TypeScript và áp dụng các kỹ thuật trên.
import React, { useCallback } from 'react';
import { FlatList, View, Text, StyleSheet } from 'react-native';
interface Item {
id: string;
title: string;
}
const data: Item[] = Array.from({ length: 1000 }, (_, i) => ({
id: `item-${i}`,
title: `Item ${i}`,
}));
const renderItem = ({ item }: { item: Item }) => (
<View style={styles.item}>
<Text>{item.title}</Text>
</View>
);
const keyExtractor = (item: Item) => item.id;
export default function OptimizedFlatList() {
const memoizedRenderItem = useCallback(renderItem, []);
const memoizedKeyExtractor = useCallback(keyExtractor, []);
return (
<FlatList
data={data}
renderItem={memoizedRenderItem}
keyExtractor={memoizedKeyExtractor}
getItemLayout={(_, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
})}
initialNumToRender={10}
maxToRenderPerBatch={5}
windowSize={5}
/>
);
}
const ITEM_HEIGHT = 50;
const styles = StyleSheet.create({
item: {
height: ITEM_HEIGHT,
justifyContent: 'center',
paddingHorizontal: 16,
borderBottomWidth: 1,
borderColor: '#eee',
},
});
Giải thích nhanh
• useCallback giữ nguyên hàm renderItem và keyExtractor qua các lần render.
• getItemLayout trả về chiều cao cố định ITEM_HEIGHT, giúp FlatList tính toán vị trí mà không cần đo.
• Các thuộc tính initialNumToRender, maxToRenderPerBatch và windowSize giảm số phần tử được render đồng thời, tối ưu bộ nhớ.
Kết luận
Áp dụng memo hoá, cấu hình hợp lý và cung cấp getItemLayout là những bước cơ bản nhưng hiệu quả để cải thiện tốc độ scroll và giảm tiêu thụ bộ nhớ của FlatList. Khi bạn đã nắm vững các kỹ thuật này, việc xây dựng các danh sách lớn trong React Native sẽ trở nên mượt mà hơn nhiều.
Để học sâu hơn và thực hành trên dự án thực tế, Tham khảo khóa học "Lập trình App Mobile với React Native + TypeScript" tại đây.








