Infinite List
FlatList wrapper with automatic load-more on scroll.
Installation#
npx @aniui/cli add infinite-listBuild UI components
Task 1
Write unit tests
Task 2
Deploy to production
Task 3
Web preview — components render natively on iOS & Android
import { InfiniteList } from "@/components/ui/infinite-list";
export function MyScreen() {
const [data, setData] = useState(initialItems);
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const loadMore = async () => {
setLoading(true);
const newItems = await fetchMore(data.length);
setData((prev) => [...prev, ...newItems]);
setHasMore(newItems.length > 0);
setLoading(false);
};
return (
<InfiniteList
data={data}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View className="p-4 border-b border-border">
<Text className="text-foreground">{item.title}</Text>
</View>
)}
onLoadMore={loadMore}
hasMore={hasMore}
loading={loading}
threshold={0.5}
/>
);
}Usage#
app/index.tsx
import { InfiniteList } from "@/components/ui/infinite-list";
export function MyScreen() {
const [data, setData] = useState(initialItems);
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const loadMore = async () => {
setLoading(true);
const newItems = await fetchMore(data.length);
setData((prev) => [...prev, ...newItems]);
setHasMore(newItems.length > 0);
setLoading(false);
};
return (
<InfiniteList
data={data}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View className="p-4 border-b border-border">
<Text className="text-foreground">{item.title}</Text>
</View>
)}
onLoadMore={loadMore}
hasMore={hasMore}
loading={loading}
threshold={0.5}
/>
);
}Props#
PropTypeDefault
dataT[]-renderItem({ item, index }) => ReactElement-keyExtractor(item, index) => string-onLoadMore() => void-hasMorebooleanfalseloadingbooleanfalsethresholdnumber0.5classNamestring-Also accepts all FlatList props.
Accessibility#
FlatListwith auto-load-more on scroll.- List items are individually focusable by screen readers.
Source#
components/ui/infinite-list.tsx
import React from "react";
import { FlatList, View, ActivityIndicator } from "react-native";
import { cn } from "@/lib/utils";
export interface InfiniteListProps<T> extends React.ComponentPropsWithoutRef<typeof FlatList<T>> {
className?: string;
data: T[];
renderItem: ({ item, index }: { item: T; index: number }) => React.ReactElement;
keyExtractor: (item: T, index: number) => string;
onLoadMore?: () => void;
hasMore?: boolean;
loading?: boolean;
threshold?: number;
}
export function InfiniteList<T>({
className,
data,
renderItem,
keyExtractor,
onLoadMore,
hasMore = false,
loading = false,
threshold = 0.5,
...props
}: InfiniteListProps<T>) {
return (
<FlatList
className={cn("", className)}
data={data}
renderItem={renderItem}
keyExtractor={keyExtractor}
onEndReached={hasMore && !loading ? onLoadMore : undefined}
onEndReachedThreshold={threshold}
ListFooterComponent={
loading ? (
<View className="py-4 items-center">
<ActivityIndicator size="small" color="hsl(240 5.9% 10%)" accessibilityRole="progressbar" />
</View>
) : null
}
{...props}
/>
);
}