Списки в React Native 2026: FlatList, FlashList v2 и Legend List — руководство по оптимизации

Сравнение FlatList, FlashList v2 и Legend List в React Native 2026. Разбираем cell recycling и виртуализацию, оптимизируем FlatList, мигрируем на FlashList v2 для New Architecture, строим чат на Legend List — с рабочими примерами кода.

Почему производительность списков — это всегда больная тема

Списки — сердце практически любого мобильного приложения. Лента новостей, каталог товаров, чат, контакты, история транзакций — всё это, по сути, списки. И в React Native эта задача сложнее, чем в чисто нативной разработке: между JS-потоком и нативным UI стоит слой абстракции, и любая неэффективность мгновенно превращается в дропы FPS и раздражающие «белые пробелы» при прокрутке.

Честно говоря, ещё пару лет назад ситуация со списками в RN была так себе. Но в 2026-м экосистема наконец-то предлагает зрелые решения.

Встроенный FlatList никуда не делся — он по-прежнему отправная точка. Но FlashList v2 от Shopify (полностью переписанный под New Architecture) и Legend List 1.0 с его уникальным подходом — это уже принципиально другой уровень. В этом руководстве разберём каждый компонент, сравним архитектуру, покажем рабочие примеры и поможем выбрать подходящий инструмент для вашего проекта.

Как работает FlatList и почему он тормозит

Архитектура виртуализации

FlatList построен поверх VirtualizedList и использует стратегию виртуализации: он рендерит только те элементы, что видны на экране, плюс небольшой буфер. Элементы, уходящие за пределы видимости, полностью размонтируются — так экономится память.

Проблема в том, что при каждом скролле новые элементы создаются с нуля. Даже если элемент уже был смонтирован раньше — FlatList пересоздаст его заново: полный цикл рендеринга, useEffect-хуки, вся логика. При быстрой прокрутке это приводит к просадкам FPS и белым пробелам (так называемые blank areas). Знакомо, правда?

Ключевые пропсы для оптимизации FlatList

Прежде чем бежать за альтернативами, стоит попробовать выжать максимум из того, что есть. Вот главные рычаги оптимизации.

getItemLayout — самая важная оптимизация

Если элементы вашего списка имеют одинаковую высоту, getItemLayout — это, пожалуй, самое значительное улучшение, которое можно сделать. Он избавляет FlatList от необходимости измерять каждый элемент асинхронно:

const ITEM_HEIGHT = 72;

const getItemLayout = (data, index) => ({
  length: ITEM_HEIGHT,
  offset: ITEM_HEIGHT * index,
  index,
});

<FlatList
  data={items}
  renderItem={renderItem}
  getItemLayout={getItemLayout}
  keyExtractor={(item) => item.id}
/>

Без getItemLayout FlatList вынужден измерять высоту каждого элемента по мере рендеринга. При быстрой прокрутке система просто не успевает — и появляются пустые промежутки.

windowSize, initialNumToRender и maxToRenderPerBatch

Эти три пропса работают в связке — они определяют, сколько элементов живёт в памяти и как быстро рендерятся новые:

<FlatList
  data={items}
  renderItem={renderItem}
  keyExtractor={(item) => item.id}
  // Количество «экранов» элементов в памяти (дефолт: 21)
  // Уменьшите до 5-7 для экономии памяти
  windowSize={5}
  // Количество элементов для первого рендера (дефолт: 10)
  // Если на экране видно 5 элементов — поставьте 5
  initialNumToRender={5}
  // Элементов за один «батч» при прокрутке (дефолт: 10)
  maxToRenderPerBatch={10}
  // Задержка между батчами в мс
  updateCellsBatchingPeriod={100}
  // Особенно полезен на Android — скрывает элементы за пределами viewport
  removeClippedSubviews={true}
/>

windowSize контролирует, сколько «экранов» контента FlatList хранит в памяти. Значение по умолчанию — 21 (это 10 экранов выше и 10 ниже текущей позиции). Для большинства приложений хватит 5–7, и это ощутимо снизит расход памяти. Правда, при очень быстрой прокрутке могут проскакивать кратковременные белые пробелы — тут уж компромисс.

Мемоизация: React.memo, useCallback и keyExtractor

Одна из самых распространённых ошибок — определять renderItem прямо внутри JSX. Это создаёт новую функцию при каждом рендере родительского компонента, и FlatList вынужден перерисовывать все видимые элементы. Казалось бы, мелочь, а на деле — серьёзный удар по производительности.

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

// Выносим элемент списка в отдельный мемоизированный компонент
const ProductItem = memo(({ item }) => (
  <View style={styles.item}>
    <Image
      source={{ uri: item.thumbnail }}
      style={styles.thumbnail}
    />
    <View style={styles.info}>
      <Text style={styles.title}>{item.name}</Text>
      <Text style={styles.price}>{item.price} ₽</Text>
    </View>
  </View>
));

export default function ProductList({ products }) {
  // Стабильная ссылка на renderItem через useCallback
  const renderItem = useCallback(
    ({ item }) => <ProductItem item={item} />,
    []
  );

  // Стабильная ссылка на keyExtractor
  const keyExtractor = useCallback((item) => item.id, []);

  return (
    <FlatList
      data={products}
      renderItem={renderItem}
      keyExtractor={keyExtractor}
      getItemLayout={(data, index) => ({
        length: 80,
        offset: 80 * index,
        index,
      })}
    />
  );
}

const styles = StyleSheet.create({
  item: { flexDirection: 'row', padding: 12, height: 80 },
  thumbnail: { width: 56, height: 56, borderRadius: 8 },
  info: { marginLeft: 12, justifyContent: 'center' },
  title: { fontSize: 16, fontWeight: '600' },
  price: { fontSize: 14, color: '#666', marginTop: 4 },
});

Важный момент: keyExtractor должен возвращать уникальный, стабильный идентификатор, а не индекс массива. Использование index.toString() — классическая ошибка, которая приводит к некорректным ре-рендерам при изменении данных.

FlashList v2: революция списков для New Architecture

Что изменилось в v2

FlashList v2 — это не просто обновление, а полная переработка с нуля, специально для New Architecture. Ключевое отличие от v1: теперь это 100% JavaScript-решение без нативных модулей. Это упрощает поддержку, а производительность при этом стала ещё лучше.

Главная фишка FlashList (и в v1, и в v2) — стратегия переработки ячеек (cell recycling). Вместо того чтобы уничтожать компоненты при скролле, FlashList поддерживает пул и переиспользует их, обновляя только данные. По сути, тот же подход, что в нативных UITableView (iOS) и RecyclerView (Android). И это работает потрясающе.

Что конкретно изменилось в v2:

  • Не нужны оценки размеров: Проп estimatedItemSize из v1 удалён — v2 автоматически измеряет элементы при первом рендере и кеширует результаты
  • Masonry-макет как проп: MasonryFlashList упразднён — теперь достаточно передать masonry={true}
  • Адаптивный алгоритм: Вместо фиксированного окна рендеринга v2 учитывает скорость и направление прокрутки, а также производительность устройства. На практике это означает до 50% меньше пустых областей при скролле
  • maintainVisibleContentPosition по умолчанию: Автоматически корректирует позицию прокрутки при добавлении элементов сверху — для чатов это просто спасение

Важный нюанс: FlashList v2 работает только с New Architecture. Если ваш проект ещё на старой — оставайтесь на FlashList v1.x.

Установка и базовый пример

# Установка
npm install @shopify/flash-list@^2.0.0
# или
yarn add @shopify/flash-list@^2.0.0

Вот базовый пример — заметьте, насколько API похож на FlatList:

import React, { useCallback } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { FlashList } from '@shopify/flash-list';

const data = Array.from({ length: 10000 }, (_, i) => ({
  id: String(i),
  title: `Элемент ${i}`,
  subtitle: `Описание элемента ${i}`,
}));

export default function PerformantList() {
  const renderItem = useCallback(({ item }) => (
    <View style={styles.item}>
      <Text style={styles.title}>{item.title}</Text>
      <Text style={styles.subtitle}>{item.subtitle}</Text>
    </View>
  ), []);

  return (
    <FlashList
      data={data}
      renderItem={renderItem}
      keyExtractor={(item) => item.id}
      // estimatedItemSize больше НЕ нужен в v2!
      // FlashList сам измерит элементы
    />
  );
}

const styles = StyleSheet.create({
  item: { padding: 16, borderBottomWidth: 1, borderBottomColor: '#eee' },
  title: { fontSize: 16, fontWeight: '600' },
  subtitle: { fontSize: 14, color: '#888', marginTop: 4 },
});

Миграция с FlashList v1 на v2

Переход с v1 на v2 не такой уж болезненный, но есть несколько вещей, которые стоит знать:

// БЫЛО (FlashList v1)
import { FlashList, MasonryFlashList } from '@shopify/flash-list';

const listRef = useRef<FlashList<ItemType>>(null);

<FlashList
  estimatedItemSize={80}        // удалить
  estimatedListSize={{ ... }}   // удалить
  inverted={true}               // заменить
  onBlankArea={callback}        // удалить
  renderItem={renderItem}
  data={data}
/>

// СТАЛО (FlashList v2)
import { FlashList, FlashListRef } from '@shopify/flash-list';

const listRef = useRef<FlashListRef<ItemType>>(null);

<FlashList
  // estimatedItemSize — удалён, v2 измеряет сам
  // inverted — удалён, используйте:
  maintainVisibleContentPosition={{ minIndexForVisible: 0 }}
  renderItem={renderItem}
  data={[...data].reverse()} // переверните данные вручную
/>

Полный список удалённых пропсов: estimatedItemSize, estimatedListSize, estimatedFirstItemOffset, inverted, onBlankArea, disableHorizontalListHeightMeasurement, disableAutoLayout. Много всего, но по факту вы просто удаляете код — а не добавляете.

Продвинутые возможности FlashList v2

Для списков с разными типами элементов используйте getItemType — он позволяет FlashList создавать отдельные пулы для переработки разных компонентов. Это важная оптимизация, которую многие почему-то пропускают:

import React, { useCallback } from 'react';
import { View, Text, Image, StyleSheet } from 'react-native';
import { FlashList } from '@shopify/flash-list';

// Разные типы элементов: заголовок секции и обычный элемент
const SectionHeader = ({ item }) => (
  <View style={styles.sectionHeader}>
    <Text style={styles.sectionTitle}>{item.title}</Text>
  </View>
);

const ProductCard = ({ item }) => (
  <View style={styles.productCard}>
    <Image source={{ uri: item.image }} style={styles.productImage} />
    <Text style={styles.productName}>{item.name}</Text>
    <Text style={styles.productPrice}>{item.price} ₽</Text>
  </View>
);

export default function CatalogList({ items }) {
  const renderItem = useCallback(({ item }) => {
    if (item.type === 'header') return <SectionHeader item={item} />;
    return <ProductCard item={item} />;
  }, []);

  // getItemType позволяет FlashList создавать отдельные пулы
  // для переработки разных типов компонентов
  const getItemType = useCallback((item) => item.type, []);

  return (
    <FlashList
      data={items}
      renderItem={renderItem}
      getItemType={getItemType}
      keyExtractor={(item) => item.id}
    />
  );
}

А для Pinterest-подобных сеток в v2 всё стало ещё проще — один проп:

<FlashList
  data={photos}
  renderItem={renderItem}
  masonry={true}
  numColumns={2}
  keyExtractor={(item) => item.id}
/>

Legend List 1.0: новый подход к спискам

Что такое Legend List

Legend List — высокопроизводительный компонент от команды LegendApp, целиком написанный на TypeScript без нативных зависимостей. Позиционируется как drop-in замена и FlatList, и FlashList.

Главная особенность — опциональная переработка. По умолчанию Legend List использует виртуализацию (как FlatList), но если включить recycleItems={true}, переключается на recycling (как FlashList). Это даёт гибкость: для простых элементов — recycling ради скорости, для сложных со своим стейтом — виртуализация ради безопасности. По-моему, очень элегантное решение.

Что ещё выделяет Legend List 1.0:

  • Двунаправленный бесконечный скролл: Прокрутка в обоих направлениях без рывков и «прыжков» контента
  • Чат-интерфейсы без инверсии: Пропсы alignItemsAtEnd и maintainScrollAtEnd позволяют строить чат без костыля с inverted, который ломает анимации
  • 100% JS: Работает и на старой, и на новой архитектуре
  • Динамические размеры: Элементы разной высоты — не проблема, и без потери производительности

Установка и примеры

# npm
npm install @legendapp/list
# yarn
yarn add @legendapp/list
# expo
npx expo install @legendapp/list

Вот базовый пример чата с Legend List:

import React, { useCallback } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { LegendList } from '@legendapp/list';

const messages = Array.from({ length: 5000 }, (_, i) => ({
  id: String(i),
  text: `Сообщение ${i}`,
  sender: i % 2 === 0 ? 'me' : 'other',
}));

export default function ChatScreen() {
  const renderItem = useCallback(({ item }) => (
    <View style={[
      styles.bubble,
      item.sender === 'me' ? styles.myBubble : styles.otherBubble,
    ]}>
      <Text style={styles.messageText}>{item.text}</Text>
    </View>
  ), []);

  return (
    <LegendList
      data={messages}
      renderItem={renderItem}
      keyExtractor={(item) => item.id}
      // Включаем recycling для производительности
      recycleItems={true}
      // Контент прижат к низу — идеально для чата
      alignItemsAtEnd={true}
      // Автоскролл при новых сообщениях
      maintainScrollAtEnd={true}
      // Корректировка позиции при добавлении элементов сверху
      maintainVisibleContentPosition={true}
    />
  );
}

const styles = StyleSheet.create({
  bubble: { padding: 12, marginVertical: 4, marginHorizontal: 16,
    borderRadius: 16, maxWidth: '75%' },
  myBubble: { backgroundColor: '#6C63FF', alignSelf: 'flex-end' },
  otherBubble: { backgroundColor: '#E8E8E8', alignSelf: 'flex-start' },
  messageText: { fontSize: 15, color: '#333' },
});

Оптимизация Legend List для тяжёлых списков

Для максимальной производительности Legend List предлагает тонкую настройку:

<LegendList
  data={items}
  renderItem={renderItem}
  keyExtractor={(item) => item.id}
  // Включить переработку компонентов
  recycleItems={true}
  // Категоризация типов для более умной переработки
  getItemType={(item) => item.type}
  // Фиксированный размер — отключает измерение, максимум производительности
  getFixedItemSize={() => 80}
  // Или оценочный размер для элементов разной высоты
  getEstimatedItemSize={() => 100}
/>

getFixedItemSize — штука мощная. Если знаете точную высоту элементов, он полностью отключает измерение, и вы получаете максимальную производительность. Для элементов с динамической высотой используйте getEstimatedItemSize — Legend List подскажет оптимальное значение в логах (удобно).

Сравнение: FlatList vs FlashList v2 vs Legend List

Итак, давайте посмотрим на всё это в одной таблице:

ПараметрFlatListFlashList v2Legend List 1.0
СтратегияВиртуализацияCell RecyclingВиртуализация + опциональный Recycling
ВстроенныйДаНетНет
Нативный кодНетНет (v2)Нет
New ArchitectureОбеТолько новаяОбе
Оценки размеровgetItemLayoutАвтоматическиОпционально
Masonry-макетНетДа (проп)Нет
Чат без инверсииНетЧастичноДа (нативно)
Двунаправленный скроллНетНетДа
ПроизводительностьБазоваяВ 5–10× быстрееСопоставимо с FlashList
Размер пакета0 КБ (встроен)СреднийМинимальный

Когда какой выбирать

FlatList — ваш выбор, если:

  • Список небольшой (до 100 элементов) и элементы простые
  • Нужны встроенные фичи: SectionList, header/footer, pull-to-refresh
  • Не хотите добавлять зависимости
  • Производительность и так устраивает

FlashList v2 — если:

  • Проект на New Architecture (React Native 0.76+)
  • Списки от 100 элементов и больше
  • Нужен masonry-макет (Pinterest-стиль)
  • Критична производительность на Android
  • Хотите проверенное решение — FlashList используется в Shopify и тысячах других приложений

Legend List — если:

  • Нужна поддержка обеих архитектур
  • Строите чат — пропсы alignItemsAtEnd и maintainScrollAtEnd тут незаменимы
  • Нужен двунаправленный бесконечный скролл
  • Хотите минимальный размер зависимости
  • Элементы со сложным локальным стейтом (можно отключить recycling)

Практические сценарии оптимизации

Сценарий 1: каталог товаров с изображениями

Каталог с карточками товаров — один из самых типичных сценариев. Главная засада здесь — тяжёлые элементы с картинками:

import React, { useCallback, memo } from 'react';
import { View, Text, Image, Pressable, StyleSheet } from 'react-native';
import { FlashList } from '@shopify/flash-list';

// Мемоизированный компонент карточки товара
const ProductCard = memo(({ item, onPress }) => (
  <Pressable onPress={() => onPress(item.id)} style={styles.card}>
    <Image
      source={{ uri: item.imageUrl }}
      style={styles.image}
      // Используйте resizeMode для оптимизации отрисовки
      resizeMode="cover"
    />
    <View style={styles.cardContent}>
      <Text style={styles.name} numberOfLines={2}>
        {item.name}
      </Text>
      <Text style={styles.price}>{item.price} ₽</Text>
      {item.discount > 0 && (
        <Text style={styles.discount}>-{item.discount}%</Text>
      )}
    </View>
  </Pressable>
));

export default function ProductCatalog({ products, onProductPress }) {
  const renderItem = useCallback(
    ({ item }) => <ProductCard item={item} onPress={onProductPress} />,
    [onProductPress]
  );

  return (
    <FlashList
      data={products}
      renderItem={renderItem}
      keyExtractor={(item) => item.id}
      numColumns={2}
      // getItemType помогает FlashList эффективнее
      // переиспользовать компоненты одного типа
      getItemType={(item) =>
        item.hasDiscount ? 'discounted' : 'regular'
      }
    />
  );
}

Сценарий 2: бесконечная лента с пагинацией

Классический паттерн бесконечной прокрутки с подгрузкой данных. Этот подход работает одинаково хорошо со всеми тремя компонентами:

import React, { useState, useCallback } from 'react';
import { View, Text, ActivityIndicator, StyleSheet } from 'react-native';
import { FlashList } from '@shopify/flash-list';

export default function InfiniteFeed() {
  const [items, setItems] = useState(initialItems);
  const [loading, setLoading] = useState(false);
  const [page, setPage] = useState(1);

  const loadMore = useCallback(async () => {
    if (loading) return;
    setLoading(true);
    try {
      const nextPage = page + 1;
      const newItems = await fetchItems(nextPage);
      setItems((prev) => [...prev, ...newItems]);
      setPage(nextPage);
    } finally {
      setLoading(false);
    }
  }, [loading, page]);

  const renderItem = useCallback(({ item }) => (
    <View style={styles.feedItem}>
      <Text style={styles.feedTitle}>{item.title}</Text>
      <Text style={styles.feedBody}>{item.body}</Text>
    </View>
  ), []);

  const renderFooter = useCallback(() => {
    if (!loading) return null;
    return (
      <View style={styles.footer}>
        <ActivityIndicator size="small" color="#6C63FF" />
      </View>
    );
  }, [loading]);

  return (
    <FlashList
      data={items}
      renderItem={renderItem}
      keyExtractor={(item) => item.id}
      onEndReached={loadMore}
      onEndReachedThreshold={0.5}
      ListFooterComponent={renderFooter}
    />
  );
}

Сценарий 3: чат-интерфейс с Legend List

Чат — пожалуй, самый сложный сценарий для списков. Традиционный подход с inverted={true} в FlatList тянет за собой кучу проблем: инвертированные жесты, сломанные анимации, неправильный порядок для accessibility. Legend List решает всё это элегантно:

import React, { useState, useCallback } from 'react';
import { View, Text, TextInput, Pressable, StyleSheet } from 'react-native';
import { LegendList } from '@legendapp/list';

export default function ChatScreen() {
  const [messages, setMessages] = useState(initialMessages);
  const [inputText, setInputText] = useState('');

  const sendMessage = useCallback(() => {
    if (!inputText.trim()) return;
    const newMsg = {
      id: Date.now().toString(),
      text: inputText,
      sender: 'me',
      timestamp: new Date(),
    };
    setMessages((prev) => [...prev, newMsg]);
    setInputText('');
  }, [inputText]);

  const renderItem = useCallback(({ item }) => (
    <View style={[
      styles.messageBubble,
      item.sender === 'me' ? styles.myMessage : styles.theirMessage,
    ]}>
      <Text style={styles.messageText}>{item.text}</Text>
      <Text style={styles.timestamp}>
        {new Date(item.timestamp).toLocaleTimeString('ru-RU', {
          hour: '2-digit', minute: '2-digit',
        })}
      </Text>
    </View>
  ), []);

  return (
    <View style={styles.container}>
      <LegendList
        data={messages}
        renderItem={renderItem}
        keyExtractor={(item) => item.id}
        recycleItems={true}
        // Контент прижат к низу — как в настоящем чате
        alignItemsAtEnd={true}
        // Автоматический скролл при новом сообщении
        maintainScrollAtEnd={true}
        // Коррекция позиции при загрузке старых сообщений сверху
        maintainVisibleContentPosition={true}
      />
      <View style={styles.inputContainer}>
        <TextInput
          style={styles.input}
          value={inputText}
          onChangeText={setInputText}
          placeholder="Сообщение..."
        />
        <Pressable onPress={sendMessage} style={styles.sendButton}>
          <Text style={styles.sendText}>→</Text>
        </Pressable>
      </View>
    </View>
  );
}

Профилирование и измерение производительности

Оптимизация без измерений — это гадание на кофейной гуще. Давайте разберём, чем конкретно измерять.

Performance Monitor

Встроенный Performance Monitor показывает FPS UI-потока и JS-потока в реальном времени. Откройте Dev Menu → «Perf Monitor». Цель — стабильные 60 FPS на обоих потоках при скролле. Если JS FPS падает ниже 45 — пора оптимизировать.

React DevTools Profiler

React DevTools Profiler — ваш друг для анализа ре-рендеров. Запускаете запись, скроллите список, останавливаете и смотрите: какие компоненты перерисовались и почему. Самая частая находка — элементы перерисовываются при каждом скролле из-за нестабильных ссылок на функции или объекты.

Советы по профилированию

  • Тестируйте на реальных устройствах: На симуляторе и мощном iPhone всё выглядит отлично — возьмите средний Android и прослезитесь
  • Тестируйте с реальным объёмом данных: 20 элементов никогда не покажут проблему — загрузите 1000+
  • Release-сборки: Debug-режим добавляет заметный оверхед, поэтому для точных замеров профилируйте релизную сборку
  • Следите за blank areas: Белые пробелы при быстрой прокрутке — главный симптом проблем с производительностью списка

Миграция с FlatList: пошаговый чеклист

Если FlatList тормозит и вы решили переезжать — вот чеклист, чтобы ничего не забыть:

  1. Определите архитектуру: New Architecture → FlashList v2 или Legend List. Старая → Legend List или FlashList v1
  2. Определите сценарий: Чат → Legend List. Каталог/лента → FlashList v2. Masonry → FlashList v2
  3. Установите пакет и замените импорт
  4. Удалите ненужные пропсы: FlashList v2 не использует getItemLayout, windowSize, initialNumToRender и другие пропсы виртуализации
  5. Добавьте keyExtractor — обязательно для обоих компонентов
  6. Для FlashList: добавьте getItemType при наличии разных типов элементов
  7. Для Legend List: включите recycleItems={true}, если элементы не содержат сложного стейта
  8. Мемоизируйте: Оберните renderItem в useCallback, компоненты элементов — в React.memo
  9. Протестируйте на реальном устройстве с большим объёмом данных

FAQ: частые вопросы

Почему FlatList тормозит при быстрой прокрутке?

Потому что он размонтирует элементы при выходе из viewport и создаёт заново при возвращении. При быстром скролле JS-поток не успевает — появляются белые пробелы и падает FPS. Первым делом оптимизируйте пропсы (getItemLayout, windowSize, maxToRenderPerBatch) и мемоизируйте компоненты. Если не помогло — переходите на FlashList или Legend List.

Можно ли использовать FlashList v2 со старой архитектурой?

Нет. FlashList v2 требует New Architecture и на старой просто не запустится. Варианты: FlashList v1.x (@shopify/flash-list@^1.0.0) или Legend List, который работает на обеих.

В чём разница между виртуализацией и cell recycling?

Виртуализация (FlatList) — это создание и уничтожение компонентов при скролле. Cell recycling (FlashList) — это переиспользование уже существующих компонентов с новыми данными. Recycling быстрее, потому что не нужно тратить ресурсы на mount/unmount. Грубо говоря, вместо «сломать и построить заново» — «поменять табличку на двери».

Как выбрать между FlashList v2 и Legend List?

FlashList v2 — более зрелый и проверенный вариант, особенно хорош для каталогов, лент и masonry. Legend List выигрывает в чат-сценариях (благодаря alignItemsAtEnd и maintainScrollAtEnd) и в проектах на старой архитектуре. Оба дают существенный прирост по сравнению с FlatList.

Нужно ли указывать estimatedItemSize в FlashList v2?

Нет, этот проп удалён. FlashList v2 измеряет элементы автоматически и кеширует результаты. Это одно из главных улучшений — больше никакого подбора «волшебных чисел». Просто не забудьте про keyExtractor.

Об авторе Editorial Team

Our team of expert writers and editors.