Олег Марков
Оптимизация отображения списков в React Native
Введение
Работа с длинными списками — один из самых распространённых и непростых аспектов разработки мобильных приложений на React Native. Если вы отображаете ленту новостей, каталог товаров или любой другой скроллируемый список, неэффективная реализация может привести к просадкам производительности, “подвисаниям” интерфейса, чрезмерному потреблению памяти. В этой статье я расскажу вам, как правильно оптимизировать отображение списков в React Native и добиться максимальной плавности даже на устройствах среднего класса.
Мы разберём основные компоненты для отображения списков, подходы к оптимизации их работы, правильную настройку ключевых параметров, способы мемоизации элементов и важные рекомендации по работе с большими данными. В статье вы найдёте фрагменты кода и подробные пояснения для практического применения.
Почему оптимизация списков важна
Большинство мобильных приложений так или иначе используют списки: сообщения, товары, задачи, медиа и многое другое. Когда количество элементов растёт до сотен или тысяч, каждое неэффективное решение начинает влиять на производительность и плавность UI:
- Долгий рендеринг приводит к фризам и статоррации.
- Избыточное потребление оперативной памяти вызывает баги и аварийные завершения.
- Высокий расход батареи раздражает пользователей.
В React Native стандартный способ отображения списков — компонент FlatList
(и устаревший ListView
). Давайте разберёмся, какие сложности с ним могут возникнуть и как их избежать.
Как работает FlatList и почему его выбирают
Основные характеристики FlatList
FlatList
в React Native был создан как альтернатива громоздкому ScrollView
для случаев, когда количество элементов велико, и все они должны прокручиваться вертикально или горизонтально. Вот основные его функции:
- Отображает только видимые элементы (виртуализация). Вместо рендеринга всех данных сразу, FlatList отображает только те элементы, которые сейчас видны на экране плюс небольшой запас (window).
- Поддержка ключей. Каждый элемент списка должен быть уникально идентифицирован для оптимального обновления UI.
- Параметризация поведения — возможность управлять памятью, частотой обновлений и поведением списка.
Примеры использования
Давайте сразу посмотрим на базовый пример FlatList:
import React from 'react';
import { FlatList, Text, View } from 'react-native';
const data = [
{ id: '1', title: 'Первый элемент' },
{ id: '2', title: 'Второй элемент' },
// ...можно добавить больше
];
const renderItem = ({ item }) => (
<View>
<Text>{item.title}</Text>
</View>
);
const MyList = () => (
<FlatList
data={data} // массив данных
renderItem={renderItem} // отображение элемента
keyExtractor={item => item.id} // уникальный ключ
/>
);
FlatList автоматически обработает виртуализацию и будет отображать только те элементы, которые находятся в области видимости скролла.
Отображение больших списков - это распространенная задача в мобильных приложениях. В React Native существуют различные техники оптимизации отображения списков, такие как виртуализация, кеширование и использование нативных драйверов. Чтобы создавать производительные приложения, важно понимать, как работает система рендеринга в React Native и уметь применять эти техники. Если вы хотите детально разобраться в оптимизации отображения списков и создавать быстрые и плавные интерфейсы — приходите на наш большой курс React Native и Expo Router. На курсе 184 уроков и 11 упражнений, AI-тренажеры для безлимитной практики с кодом и задачами 24/7, решение задач с живым ревью наставника, еженедельные встречи с менторами.
Основные приёмы оптимизации FlatList
Давайте более подробно изучим, как на практике добиться максимальной производительности при работе со списками. Здесь собраны лучшие техники, которыми пользуются в продакшн-проектах.
1. Используйте FlatList вместо ScrollView для больших списков
ScrollView
рендерит все элементы сразу — для длинных списков это прямой путь к замедлению работы приложения и переполнению памяти. Всегда используйте FlatList
или SectionList
для больших коллекций.
2. Выбирайте правильный keyExtractor
React требует уникальный ключ для каждого элемента. Так вы избежите лишнего ререндера элементов на обновлениях. Например:
<FlatList
data={items}
renderItem={renderItem}
keyExtractor={item => item.id.toString()}
/>
Если используете индекс в массиве, старайтесь делать это только там, где данные действительно не изменяются (нет вставок, удалений или перемещений).
3. Добавляйте getItemLayout для стабильной производительности
Если элементы списка имеют одинаковую высоту, явно указывайте это через свойство getItemLayout
. Это сильно ускоряет вычисление позиций и прокрутку:
<FlatList
data={items}
renderItem={renderItem}
getItemLayout={(data, index) => (
{length: 50, offset: 50 * index, index}
)}
/>
Здесь мы говорим FlatList, что каждый элемент высотой 50
4. Используйте memo для элементов списка
Дополнительная мемоизация компонентов-элементов через React.memo или PureComponent предотвращает их ненужную перерисовку:
const MemoizedItem = React.memo(({ item }) => (
<View>
<Text>{item.title}</Text>
</View>
));
Теперь компонент будет обновляться только если реально изменились props
5. Контролируйте размер окна и буфера (windowSize, initialNumToRender)
Виртуализация работает с понятием “окна видимости” (window), то есть сколько элементов “рядом” с видимым экраном должно быть отрендерено заранее.
initialNumToRender
— сколько элементов показать изначально.windowSize
— величина окна для предварительного рендера.maxToRenderPerBatch
— максимальное число новых элементов в одном рендере.
Пример настройки:
<FlatList
data={items}
renderItem={renderItem}
initialNumToRender={10}
windowSize={5}
maxToRenderPerBatch={10}
/>
Так вы ограничите количество одновременно отображаемых элементов.
6. Удаляйте невидимые элементы из памяти (removeClippedSubviews)
Если ваши элементы сложные по структуре, используйте removeClippedSubviews
:
<FlatList
data={items}
renderItem={renderItem}
removeClippedSubviews={true}
/>
Теперь компоненты вне области видимости будут удаляться из DOM.
7. Тщательно оптимизируйте колбек renderItem
Избегайте создания функций или объектов внутри renderItem. Лучше оформить его как отдельную мемоизированную функцию.
const renderItem = React.useCallback(
({ item }) => <MemoizedItem item={item} />,
[]
);
Это помогает FlatList избежать лишних перерасчетов.
Передовые техники оптимизации списков
Применяйте shouldComponentUpdate / React.memo для сложных элементов
Если элемент списка реализован как сложный класс, обязательно переопределяйте shouldComponentUpdate
. Для функциональных компонентов используйте обертку React.memo
и явно проверяйте значимые props.
Разделяйте длинные списки по секциям — SectionList
Когда в вашем приложении появляется логическая группировка элементов (например, дата или категория), воспользуйтесь SectionList
. Это даст больше контроля и ускоряет рендеринг при большом количестве элементов.
<SectionList
sections={[{title: 'A', data: [...]}, {title: 'B', data: [...]}]}
renderItem={renderItem}
renderSectionHeader={({section}) => <Text>{section.title}</Text>}
/>
Используйте lazy-loading и пагинацию
Для крайне длинных списков предусмотрите “ленивую” подгрузку данных (infinite scroll, пагинация). Обычно это реализуется через событие прокрутки FlatList:
const [page, setPage] = React.useState(1);
const handleEndReached = () => {
// Функция вызовется, когда скролл достиг конца
setPage(prev => prev + 1);
// Подгружаете новую порцию данных
};
<FlatList
data={items}
renderItem={renderItem}
onEndReached={handleEndReached}
onEndReachedThreshold={0.5}
/>
Значение onEndReachedThreshold — как далеко до конца списка начинать подгрузку (0.5 — за 50% до конца).
Старайтесь упрощать структуру элементов
- Минимизируйте вложенность в JSX каждого элемента списка.
- Сократите использование глобальных состояний и контекстов внутри элементов.
- Не передавайте в элементы тяжелые объекты, массивы или функции без мемоизации.
Практические кейсы и дополнительные советы
Использование индикаторов загрузки и placeholder’ов
Когда данные еще не загружены, используйте свойство ListEmptyComponent
для отображения состояния “пусто”, а для отображения прогресса во время подгрузки — ListFooterComponent
:
<FlatList
data={items}
renderItem={renderItem}
ListEmptyComponent={<Text>Нет данных</Text>}
ListFooterComponent={isLoading ? <ActivityIndicator /> : null}
/>
Анимации во FlatList
Для плавности переходов используйте библиотеку react-native-reanimated
или интеграции с Animated
. Однако помните, что анимации в элементах списка должны быть лёгкими с точки зрения обновлений состояния, иначе пострадает производительность.
Наблюдайте за производительностью (Performance Monitor)
Включайте Performance Monitor в эмуляторе или на реальном устройстве через Developer Menu, чтобы видеть количество JS FPS и UI FPS. Следите за просадками!
Общие ошибки при работе со списками и как их избежать
- Рендерите массивы через map вместо FlatList — не делайте этого для длинных коллекций.
- Забываете про keyExtractor — без уникального ключа нет оптимального обновления.
- Передаёте тяжелые данные через props — правильно мемоизируйте объекты/функции.
- Слишком маленькая или наоборот большая windowSize — это нужно подбирать экспериментально.
- Отсутствие очистки данных при размонтировании — могут “зависать” тяжелые объекты и сборка мусора не сработает.
Заключение
Оптимизация отображения списков в React Native — это комплекс мер, направленных на достижение высокой производительности, минимизацию потребления памяти и достижение максимально плавного пользовательского интерфейса. Используйте FlatList и SectionList для отображения больших коллекций данных, правильно настраивайте ключевые параметры виртуализации, мемоизируйте элементы списка, подумайте о ленивой подгрузке и помните о необходимости измерять производительность на реальных устройствах.
Оптимизация списков - это важный, но не единственный аспект разработки качественного React Native приложения. Для создания полноценного приложения необходимо освоить множество других технологий и подходов, включая работу с UI, данными и нативными функциями. Курс React Native и Expo Router поможет вам в этом. В первых 3 модулях уже доступно бесплатное содержание — начните погружаться в мир React Native прямо сегодня.
Частозадаваемые технические вопросы по теме статьи и ответы на них
Как плавно прокрутить FlatList к определённому элементу?
Воспользуйтесь методом scrollToIndex
у FlatList. Сначала получите ref:
const listRef = useRef(null);
<FlatList ref={listRef} ... />
// Для прокрутки:
listRef.current.scrollToIndex({index: 10, animated: true});
Если элемент отсутствует на экране — добавьте реализацию getItemLayout.
Как заставить FlatList обновиться при изменении данных?
Если вы обновили массив данных (например, через setState), FlatList сама обновит отображаемые элементы. Если этого не происходит, проверьте правильность передачи keyExtractor и убедитесь, что data передаётся как новый массив, а не мутабельно изменяется.
Как ограничить количество параллельно загружаемых изображений в списке?
Используйте внешнюю библиотеку, например, react-native-fast-image или самостоятельно реализуйте очередь загрузки и отображения, используя состояние и LazyLoad принцип (отображайте только когда элемент на экране).
Почему FlatList тормозит на Android, хотя на iOS работает плавно?
Проверьте параметры windowSize и maxToRenderPerBatch — на Android оптимальное значение windowSize может быть выше, чем на iOS. Также убедитесь, что removeClippedSubviews включён.
Как реализовать разделители между элементами FlatList?
Используйте свойство ItemSeparatorComponent:
<FlatList
data={items}
renderItem={renderItem}
ItemSeparatorComponent={() => <View style={{height: 1, backgroundColor: 'gray'}} />}
/>
Теперь между каждым элементом будет добавлен невидимый View-разделитель.
Постройте личный план изучения React-native до уровня Middle — бесплатно!
React-native — часть карты развития Mobile
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Бесплатные лекции
Все гайды по React-native
Лучшие курсы по теме

React Native и Expo Router
Антон Ларичев
Основы JavaScript
Антон Ларичев