Эта страница переведена машинным способом и может содержать ошибки. Читать оригинал на английском

API

List

nsh экспортирует List: виртуализированный список на нативном scroll view. Существуют только строки рядом с областью просмотра, а их ячейки переиспользуются при прокрутке. Какие строки существуют, где они находятся, колонки, закреплённые заголовки и видимость вычисляются нативно, поэтому прокрутка списка из десяти тысяч элементов не запускает JS, кроме привязок строк, которые меняются при переиспользовании ячейки.

import { List, type ListProps, type ViewToken, type ViewabilityConfig } from "nsh";

Руководство см. в разделе Списки, а свойства элемента — в <List>.

#List

function List<T>(props: ListProps<T>): JSX.Element;
import { List, mount, Text, View } from "nsh";

const contacts = Array.from({ length: 1000 }, (_, i) => ({ id: String(i), name: `Contact ${i}` }));

function App() {
  return (
    <List each={contacts} estimatedItemSize={48} style={{ flex: 1 }}>
      {(item, index) => (
        <View style={{ height: 48, paddingHorizontal: 16, justifyContent: "center", backgroundColor: index() % 2 ? "#f8fafc" : "#fff" }}>
          <Text>{item().name}</Text>
        </View>
      )}
    </List>
  );
}

mount(App);

#Строки переиспользуются

children — это функция, которая отрисовывает одну ячейку. Она выполняется один раз на ячейку, а не на элемент: когда ячейка уходит из области просмотра, она переиспользуется для другого элемента, и item() и index() (оба — аксессоры) меняются. Читайте их внутри JSX-выражений ({item().name}), чтобы ячейка обновлялась при переиспользовании.

#Props

ListProps<T> объединяет собственные свойства списка, свойства прокрутки, общие с <Scroll>, и свойства, которые принимает любой элемент; последние передаются в scroll view списка.

#Данные и отрисовка

СвойствоТипПо умолчаниюОписание
eachreadonly T[](обязательно)Элементы. Передайте значение сигнала (each={items()}), чтобы отслеживать изменения; вставка элементов выше видимых строк оставляет эти строки на месте.
children(item: () => T, index: () => number) => JSX.Element(обязательно)Отрисовывает одну строку (см. выше).
estimatedItemSizenumber50Размер ещё не измеренных строк в пунктах вдоль оси списка. Близкие оценки делают индикатор прокрутки и scrollToIndex точнее.
numColumnsnumber1Элементов в строке (вертикальные списки): сетка.
horizontalbooleanfalseПрокрутка в горизонтальном направлении.
invertedbooleanfalseРаскладывает элементы с конца, элемент 0 последним (как в чате); начинается с прокрутки до конца и сохраняет расстояние до конца при изменении содержимого.
stickyHeaderIndicesnumber[]Элементы, которые закрепляются в начале при прокрутке мимо них (одна колонка, не инвертированный список).
headerListSlotОтрисовывается перед элементами.
footerListSlotОтрисовывается после элементов.
emptyListSlotПоказывается вместо элементов, пока each пуст.
separatorListSlotОтрисовывается между элементами (одна колонка).
keyExtractor(item: T, index: number) => stringitem.key, item.id или индексКлюч элемента в токенах видимости.
initialScrollIndexnumberПрокручивает к этому элементу до первого кадра.
contentStyleListContentStyleОтступы и промежутки содержимого: padding, paddingHorizontal, paddingVertical, paddingTop, paddingRight, paddingBottom, paddingLeft, gap, rowGap, columnGap (числа).
contentClassstringОтступы и промежутки содержимого в виде классов (p-*, gap-*); contentStyle имеет приоритет над ними.
styleStyleProp<ViewStyle>Собственный стиль списка (задайте ему размер: flex: 1 или высоту).
testIDstringИдентификатор, по которому UI-тесты находят scroll view списка (id: у nsh test, XCUITest, UI Automator).
refListRef | ((node: ListRef) => void)См. методы ref.

ListSlot — это компонент (Header) или элемент (<Header />).

#Загрузка, обновление и видимость

СвойствоТипПо умолчаниюОписание
onLoadMore() => voidВызывается, когда до конца остаётся не более loadMoreThreshold областей просмотра: загрузите ещё.
loadMoreThresholdnumber1Насколько близко к концу (в длинах области просмотра) срабатывает onLoadMore.
onLoadMoreAtStart() => voidВызывается, когда до начала остаётся не более loadMoreAtStartThreshold областей просмотра после того, как пользователь или команда прокрутки сдвинули список: загрузите то, что идёт перед первым элементом.
loadMoreAtStartThresholdnumber1Насколько близко к началу (в длинах области просмотра) срабатывает onLoadMoreAtStart.
refreshingbooleanПоказывается индикатор pull-to-refresh.
onRefresh() => voidВключает pull to refresh; установите refreshing на время загрузки.
refreshIndicator{ height: number }Приложение рисует собственный индикатор pull-to-refresh высотой height вместо стандартного спиннера.
pullValueAnimatedValueПолучает значение того, насколько список оттянут за верхний край, для этого индикатора.
onViewableItemsChange(info: { viewableItems: ViewToken<T>[]; changed: ViewToken<T>[] }) => voidЭлементы, которые стали видимыми или перестали быть видимыми.
viewabilityConfigViewabilityConfigКогда элемент считается видимым.
interface ViewToken<T> {
  item: T;
  key: string;
  index: number;
  isViewable: boolean;
}

interface ViewabilityConfig {
  /** How much of the viewport an item must cover to count as viewable, from 0 to 1. */
  viewAreaCoverageThreshold?: number;
  /** How much of an item must be visible, from 0 to 1. Without either threshold, any visible pixel counts. */
  itemVisibleThreshold?: number;
  /** Milliseconds items must stay viewable before they are reported. */
  minimumViewTime?: number;
}

#Прокрутка

СвойствоТипПо умолчаниюОписание
scrollbarboolean | ScrollbarThemetrueИндикатор прокрутки показывается: платформенный или встроенная полоса приложения; объект — это тема собственного индикатора view.
scrollablebooleantrueПользователь может прокручивать.
bouncesbooleantrueПрокрутка за края (overscroll).
pagingEnabledbooleanfalseОстанавливается на кратных значениях размера области просмотра.
snapToIntervalnumber0Останавливается на кратных значениях этой длины (0: выключено).
snapToOffsetsnumber[]Останавливается на этих смещениях.
snapToAlignment"start" | "center" | "end""start"Какая точка элемента выравнивается с областью просмотра при snapToInterval.
disableIntervalMomentumbooleanfalseБросок (fling) перемещает не более чем на одну точку привязки.
decelerationRate"normal" | "fast" | numberплатформенноеКак быстро замедляется бросок (0.998 / 0.99 или скорость, сохраняемая за миллисекунду).
contentInsetPartial<Edges>Пространство вокруг содержимого.
contentOffset{ x?, y? }Откуда оно начинается.
keyboardShouldPersistTaps"never" | "always" | "handled" | boolean"never"Скрывает ли нажатие клавиатуру ("never": нажатие только скрывает её).
keyboardDismissMode"none" | "on-drag" | "interactive""none"Скрывает ли перетаскивание клавиатуру.
scrollEventThrottlenumberМинимум миллисекунд между событиями onScroll (в любом случае не более одного за кадр).
scrollValueAnimatedValueАнимированное значение, которое получает смещение прокрутки (по главной оси) в UI-потоке, для эффектов, привязанных к прокрутке.

#События прокрутки

СобытиеДанныеКогда
onScrollScrollEvent: { offset, contentSize, size, zoomScale }Во время прокрутки.
onScrollDragStart{ offset }Начинается перетаскивание.
onScrollDragEnd{ offset }Палец отрывается.
onMomentumStart{ offset }После перетаскивания начинается скольжение.
onMomentumEnd{ offset }Скольжение заканчивается (в том числе когда его перехватывает новое перетаскивание).
onScrollEnd{ offset }Прокрутка остановилась.

Списки также принимают свойства, которые принимает любой элемент (общие свойства).

#Методы ref

ref списка получает ListRef со следующими методами в дополнение к методам ViewRef:

МетодОписание
scrollToIndex({ index, animated?, viewPosition?, viewOffset? })Прокручивает так, чтобы элемент index оказался в области просмотра. viewPosition определяет положение: 0 в начале области просмотра (по умолчанию), 0.5 посередине, 1 в конце; viewOffset сдвигает его дополнительно. Вычисляется нативно по измеренным (или оценочным) размерам строк.
scrollToOffset({ offset, animated? })Прокручивает к смещению содержимого вдоль оси списка.
scrollToEnd({ animated? })Прокручивает к концу содержимого (к началу инвертированного списка).
scrollTo({ x?, y?, animated? })Прокручивает к смещению.
import { List, type ListRef, Text, View } from "nsh";

let list!: ListRef;

<View style={{ flex: 1 }}>
  <Text onPress={() => list.scrollToIndex({ index: 60, viewPosition: 0.5 })}>Jump to 60</Text>
  <List ref={list} each={items} numColumns={3} estimatedItemSize={90} style={{ flex: 1 }}
        contentStyle={{ padding: 12, gap: 8 }}>
    {(item) => <View style={{ height: 90, backgroundColor: "#e0e7ff" }}><Text>{item().title}</Text></View>}
  </List>
</View>

#Примеры

Чат: новейшее сообщение внизу, более старые сообщения подгружаются при прокрутке вверх.

<List
  each={messages()}
  inverted
  estimatedItemSize={44}
  style={{ flex: 1 }}
  contentStyle={{ padding: 12, gap: 6 }}
  keyboardDismissMode="interactive"
  onLoadMore={loadOlder}
  loadMoreThreshold={0.5}
>
  {(m) => (
    <View style={{ alignSelf: m().mine ? "flex-end" : "flex-start", maxWidth: "75%", padding: 10, borderRadius: 14, backgroundColor: m().mine ? "#6366f1" : "#e2e8f0" }}>
      <Text style={{ color: m().mine ? "#fff" : "#0f172a" }}>{m().text}</Text>
    </View>
  )}
</List>

Pull to refresh:

const refreshing = signal(false);
const refresh = async () => {
  refreshing.set(true);
  items.set(await load());
  refreshing.set(false);
};

<List each={items()} refreshing={refreshing()} onRefresh={refresh} style={{ flex: 1 }}>
  {(item) => <Text style={{ padding: 16 }}>{item().title}</Text>}
</List>

#ListItemsProps

type ListItemsProps<T> = Pick<ListProps<T>,
  "each" | "children" | "keyExtractor" | "stickyHeaderIndices" | "initialScrollIndex"
  | "onViewableItemsChange" | "viewabilityConfig" | "refreshing" | "onRefresh"
  | "onLoadMore" | "loadMoreThreshold"
  | "header" | "footer" | "empty">;

Половина ListProps, отвечающая за данные и строки: то, что вы передаёте в собственный компонент, который сам раскладывает список, например в список настроек, оборачивающий List со своими отступами и разделителями.