API
List
nsh exporta List: uma lista virtualizada sobre uma scroll view nativa. Apenas as linhas próximas da viewport existem, e suas células são reutilizadas conforme você rola. Quais linhas existem, onde estão, colunas, cabeçalhos fixos e visibilidade são todos calculados nativamente, de modo que rolar uma lista com dez mil itens não executa nenhum JS, exceto os bindings das linhas que mudam quando uma célula é reutilizada.
import { List, type ListProps, type ViewToken, type ViewabilityConfig } from "nsh";
Veja Listas para o guia e <List> para as props do elemento.
#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);
#As linhas são recicladas
children é uma função que renderiza uma célula. Ela é executada uma vez por célula, não uma vez por item: quando uma célula sai da área visível, ela é reutilizada para outro item, e item() e index() (ambos accessors) mudam. Leia-os dentro de expressões JSX ({item().name}) para que a célula seja atualizada quando for reutilizada.
#Props
ListProps<T> combina as props próprias da lista com as props de rolagem compartilhadas com <Scroll> e as props que todo elemento aceita, que são repassadas à scroll view da lista.
#Dados e renderização
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
each |
readonly T[] |
(obrigatório) | Os itens. Passe o valor de um signal (each={items()}) para acompanhar as mudanças; inserir itens acima das linhas visíveis mantém essas linhas no lugar. |
children |
(item: () => T, index: () => number) => JSX.Element |
(obrigatório) | Renderiza uma linha (veja acima). |
estimatedItemSize |
number |
50 |
O tamanho das linhas ainda não medidas, em pontos, ao longo do eixo da lista. Estimativas próximas tornam o indicador de rolagem e scrollToIndex mais precisos. |
numColumns |
number |
1 |
Itens por linha (listas verticais): uma grade. |
horizontal |
boolean |
false |
Rola na horizontal. |
inverted |
boolean |
false |
Faz o layout a partir do fim, com o item 0 por último (estilo chat); começa rolada até o fim e mantém sua distância do fim conforme o conteúdo muda. |
stickyHeaderIndices |
number[] |
Itens que ficam fixos no início enquanto a rolagem passa por eles (uma coluna, não invertida). | |
header |
ListSlot |
Renderizado antes dos itens. | |
footer |
ListSlot |
Renderizado depois dos itens. | |
empty |
ListSlot |
Exibido no lugar dos itens enquanto each estiver vazio. |
|
separator |
ListSlot |
Renderizado entre os itens (uma coluna). | |
keyExtractor |
(item: T, index: number) => string |
item.key, item.id ou o índice |
A chave de um item nos tokens de visibilidade. |
initialScrollIndex |
number |
Rola até este item antes do primeiro frame. | |
contentStyle |
ListContentStyle |
Padding e espaçamentos do conteúdo: padding, paddingHorizontal, paddingVertical, paddingTop, paddingRight, paddingBottom, paddingLeft, gap, rowGap, columnGap (números). |
|
contentClass |
string |
Padding e espaçamentos do conteúdo como classes (p-*, gap-*); contentStyle tem precedência sobre elas. |
|
style |
StyleProp<ViewStyle> |
O estilo próprio da lista (defina um tamanho: flex: 1 ou uma altura). |
|
testID |
string |
O id pelo qual os testes de UI encontram a scroll view da lista (id: de nsh test, XCUITest, UI Automator). |
|
ref |
ListRef | ((node: ListRef) => void) |
Veja métodos de ref. |
ListSlot é um componente (Header) ou um elemento (<Header />).
#Carregamento, atualização e visibilidade
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
onLoadMore |
() => void |
Chamado quando o fim está a loadMoreThreshold viewports de distância: carrega mais. |
|
loadMoreThreshold |
number |
1 |
A que distância do fim (em comprimentos de viewport) onLoadMore é disparado. |
onLoadMoreAtStart |
() => void |
Chamado quando o início está a loadMoreAtStartThreshold viewports de distância depois que o usuário ou um comando de rolagem moveu a lista: carrega o que vem antes do primeiro item. |
|
loadMoreAtStartThreshold |
number |
1 |
A que distância do início (em comprimentos de viewport) onLoadMoreAtStart é disparado. |
refreshing |
boolean |
O spinner de pull-to-refresh é exibido. | |
onRefresh |
() => void |
Habilita o pull to refresh; defina refreshing enquanto carrega. |
|
refreshIndicator |
{ height: number } |
O app desenha seu próprio indicador de pull-to-refresh, com height de altura, no lugar do spinner. |
|
pullValue |
AnimatedValue |
Recebe o quanto a lista foi puxada além do topo, para esse indicador. | |
onViewableItemsChange |
(info: { viewableItems: ViewToken<T>[]; changed: ViewToken<T>[] }) => void |
Itens que passaram a ser visíveis ou deixaram de ser. | |
viewabilityConfig |
ViewabilityConfig |
Quando um item é considerado visível. |
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;
}
#Rolagem
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
scrollbar |
boolean | ScrollbarTheme |
true |
O indicador de rolagem é exibido: o da plataforma ou a barra integrada do app; um objeto é um tema da própria view. |
scrollable |
boolean |
true |
O usuário pode rolar. |
bounces |
boolean |
true |
Overscroll além das bordas. |
pagingEnabled |
boolean |
false |
Para em múltiplos da viewport. |
snapToInterval |
number |
0 |
Para em múltiplos deste comprimento (0: desligado). |
snapToOffsets |
number[] |
Para nestes offsets. | |
snapToAlignment |
"start" | "center" | "end" |
"start" |
Qual ponto de um item se alinha com a viewport com snapToInterval. |
disableIntervalMomentum |
boolean |
false |
Um fling move no máximo um ponto de parada. |
decelerationRate |
"normal" | "fast" | number |
plataforma | Quão rápido um fling desacelera (0.998 / 0.99, ou a velocidade mantida por milissegundo). |
contentInset |
Partial<Edges> |
Espaço ao redor do conteúdo. | |
contentOffset |
{ x?, y? } |
Onde ela começa. | |
keyboardShouldPersistTaps |
"never" | "always" | "handled" | boolean |
"never" |
Se um toque dispensa o teclado ("never": o toque apenas o dispensa). |
keyboardDismissMode |
"none" | "on-drag" | "interactive" |
"none" |
Se arrastar dispensa o teclado. |
scrollEventThrottle |
number |
Mínimo de milissegundos entre eventos onScroll (de qualquer forma, no máximo um por frame). |
|
scrollValue |
AnimatedValue |
Um valor animado alimentado com o offset de rolagem (eixo principal) na UI thread, para efeitos vinculados à rolagem. |
#Eventos de rolagem
| Evento | Payload | Quando |
|---|---|---|
onScroll |
ScrollEvent: { offset, contentSize, size, zoomScale } |
Durante a rolagem. |
onScrollDragStart |
{ offset } |
Um arrasto começa. |
onScrollDragEnd |
{ offset } |
O dedo é levantado. |
onMomentumStart |
{ offset } |
Um deslizamento começa após o arrasto. |
onMomentumEnd |
{ offset } |
O deslizamento termina (também quando um novo arrasto o interrompe). |
onScrollEnd |
{ offset } |
A rolagem parou. |
As listas também aceitam as props que todo elemento aceita (props comuns).
#Métodos de ref
O ref de uma lista recebe um ListRef, com estes métodos além dos de ViewRef:
| Método | Descrição |
|---|---|
scrollToIndex({ index, animated?, viewPosition?, viewOffset? }) |
Rola o item index até a área visível. viewPosition o posiciona: 0 no início da viewport (padrão), 0.5 no meio, 1 no fim; viewOffset o desloca ainda mais. Resolvido nativamente com os tamanhos de linha medidos (ou estimados). |
scrollToOffset({ offset, animated? }) |
Rola até um offset do conteúdo ao longo do eixo da lista. |
scrollToEnd({ animated? }) |
Rola até o fim do conteúdo (o início de uma lista invertida). |
scrollTo({ x?, y?, animated? }) |
Rola até um offset. |
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>
#Exemplos
Um chat: mensagem mais recente na parte inferior, mensagens mais antigas carregadas conforme você rola para cima.
<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">;
A metade de dados e linhas de ListProps: o que você passa a um componente seu que faz o layout de uma lista por conta própria, como uma lista de configurações que envolve List com seu próprio padding e divisores.