Giới thiệu

Trong các ứng dụng di động React Native, FlatList là thành phần danh sách được sử dụng phổ biến vì khả năng render lazy và hỗ trợ dữ liệu lớn. Tuy nhiên, khi danh sách chứa hàng nghìn mục, hiệu suất có thể giảm đáng kể nếu không áp dụng các kỹ thuật tối ưu. Bài viết này sẽ hướng dẫn chi tiết cách tối ưu FlatList khi viết bằng TypeScript, giúp giảm thời gian render, giảm tiêu thụ bộ nhớ và mang lại trải nghiệm mượt mà cho người dùng.

Nguyên nhân thường gặp gây chậm

Một số yếu tố thường làm FlatList chậm:

  • Render lại toàn bộ danh sách mỗi khi state thay đổi.
  • Không cung cấp keyExtractor đúng, dẫn tới việc React phải tính toán lại cây DOM.
  • Không xác định chiều cao cố định của mỗi mục, khiến VirtualizedList phải đo lại kích thước mỗi lần scroll.
  • Số lượng mục render đồng thời quá lớn (initialNumToRender, maxToRenderPerBatch).

Các kỹ thuật tối ưu quan trọng

1. Sử dụng keyExtractor ổn định

Đảm bảo mỗi mục có key duy nhất và không thay đổi trong suốt vòng đời của danh sách. Thông thường, sử dụng id từ dữ liệu.

2. Xác định chiều cao cố định với getItemLayout

Khi chiều cao của mục là cố định, FlatList có thể tính toán vị trí của các mục mà không cần đo lại. Điều này giảm đáng kể thời gian tính toán khi scroll.

3. Memo hoá hàm render

Sử dụng React.memo cho hàm renderItem để tránh render lại các mục không thay đổi.

4. Điều chỉnh các tham số render

Các thuộc tính initialNumToRender, maxToRenderPerBatchwindowSize giúp kiểm soát số mục được render đồng thời. Giá trị phù hợp tùy thuộc vào độ phức tạp UI và khả năng của thiết bị.

5. Kiểm tra hiệu suất với Flipper

Công cụ Flipper cung cấp tab Performance để đo thời gian render và memory usage của FlatList. Hãy đo sau mỗi thay đổi để xác nhận cải thiện.

Một ví dụ thực tế

Dưới đây là đoạn code tối ưu một FlatList hiển thị 1000 mục, sử dụng TypeScript và các kỹ thuật đã nêu.

import React, { memo } from 'react';
import { FlatList, View, Text, StyleSheet, ListRenderItemInfo } from 'react-native';

type Item = {
  id: string;
  title: string;
};

const DATA: Item[] = Array.from({ length: 1000 }, (_, i) => ({
  id: i.toString(),
  title: `Item ${i + 1}`,
}));

const renderItem = memo(({ item }: ListRenderItemInfo) => (
  <View style={styles.item}>
    <Text>{item.title}</Text>
  </View>
));

export default function OptimizedFlatList() {
  return (
    <FlatList
      data={DATA}
      keyExtractor={item => item.id}
      renderItem={renderItem}
      initialNumToRender={10}
      maxToRenderPerBatch={5}
      windowSize={5}
      getItemLayout={(_, index) => ({
        length: ITEM_HEIGHT,
        offset: ITEM_HEIGHT * index,
        index,
      })}
    />
  );
}

const ITEM_HEIGHT = 50;

const styles = StyleSheet.create({
  item: {
    height: ITEM_HEIGHT,
    justifyContent: 'center',
    paddingHorizontal: 16,
    borderBottomWidth: 1,
    borderColor: '#ccc',
  },
});

Kết luận

Bằng cách áp dụng keyExtractor ổn định, xác định chiều cao cố định qua getItemLayout, memo hoá hàm render và tinh chỉnh các tham số render, FlatList trong React Native sẽ hoạt động nhanh hơn đáng kể, ngay cả khi dữ liệu lên tới hàng ngàn mục. Đừng quên đo lại hiệu suất bằng Flipper để xác nhận kết quả.

Để nắm vững toàn bộ quy trình phát triển ứng dụng di động chuyên nghiệp, bạn có thể Tham khảo khóa học "Lập trình App Mobile với React Native + TypeScript" tại đây.