логотип PurpleSchool
логотип PurpleSchool

Оптимизация отображения списков в 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. Следите за просадками!

Общие ошибки при работе со списками и как их избежать

  1. Рендерите массивы через map вместо FlatList — не делайте этого для длинных коллекций.
  2. Забываете про keyExtractor — без уникального ключа нет оптимального обновления.
  3. Передаёте тяжелые данные через props — правильно мемоизируйте объекты/функции.
  4. Слишком маленькая или наоборот большая windowSize — это нужно подбирать экспериментально.
  5. Отсутствие очистки данных при размонтировании — могут “зависать” тяжелые объекты и сборка мусора не сработает.

Заключение

Оптимизация отображения списков в 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-разделитель.

Стрелочка влевоРуководство по navigation в React NativeКак реализовать linking на React NativeСтрелочка вправо

Постройте личный план изучения React-native до уровня Middle — бесплатно!

React-native — часть карты развития Mobile

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по React-native

Работа со ScrollView в React NativeРабота с видео в React NativeКак реализовать аудиоплеер на React NativeНастройка и использование input и textinput в React NativeИнтеграция видео плеера в приложение на React NativeИспользование выпадающих списков в React NativeСоздание и настройка native module на React NativeКак создать модальные окна в React NativeРабота с изображениями в React NativeОтображение списков данных в React NativeГайд по файловой системе в React NativeИнтеграция камеры в приложение на React NativeСоздание интерактивных кнопок в React Native
Как использовать векторные иконки в React Native5 популярных библиотек UI компонентов React NativeСоздание и использование tabs в React NativeРуководство по стилизации компонентов в React NativeОптимизация переходов между экранами в React NativeАдаптация safe area context на React NativeОбзор библиотек для навигации в React NativeСоздание сложных анимаций (reanimated) на React NativeИспользование библиотеки стилей Paper в React NativeРуководство по navigation в React NativeОптимизация отображения списков в React NativeКак реализовать linking на React NativeГайд по UI-китам для React NativeГде искать elements для приложения на React NativeРабота с цветами в React Native
Открыть базу знаний

Лучшие курсы по теме

изображение курса

React Native и Expo Router

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Основы JavaScript

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий