From 431dcded7f1beab110c8b01388e8ce28aab0daac Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 09:01:33 +0000 Subject: [PATCH 1/4] feat: add MiniFlatList, a minimal FlatList-like list Builds on NaiveList with the core ideas of React Native's FlatList: - keyExtractor (default key, then id, then index) keys items and their cached sizes, so sizes survive inserts and reorders. Unmeasured items count as the average measured size. - initialNumToRender items are rendered before the viewport is measured. - windowSize sets the rendered area in viewports. - Batched rendering: the scroll offset lives in a ref, so scrolling does not re-render while the visible items are rendered. The off-screen area fills maxToRenderPerBatch items every updateCellsBatchingPeriod ms; a blank visible area renders right away. - Cells are memoized and re-render only when their props or extraData change. - Offsets are looked up with binary search. The example app can switch between NaiveList and MiniFlatList. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EZxDtj1CphSjr2V67rdmFP --- README.md | 30 +++ example/src/App.tsx | 46 +++- src/MiniFlatList/MiniFlatList.tsx | 241 ++++++++++++++++++ .../__tests__/MiniFlatList.test.tsx | 196 ++++++++++++++ .../__tests__/item-offsets.test.ts | 60 +++++ .../__tests__/render-window.test.ts | 91 +++++++ src/MiniFlatList/index.ts | 6 + src/MiniFlatList/item-offsets.ts | 99 +++++++ src/MiniFlatList/render-window.ts | 120 +++++++++ src/index.tsx | 5 + 10 files changed, 883 insertions(+), 11 deletions(-) create mode 100644 src/MiniFlatList/MiniFlatList.tsx create mode 100644 src/MiniFlatList/__tests__/MiniFlatList.test.tsx create mode 100644 src/MiniFlatList/__tests__/item-offsets.test.ts create mode 100644 src/MiniFlatList/__tests__/render-window.test.ts create mode 100644 src/MiniFlatList/index.ts create mode 100644 src/MiniFlatList/item-offsets.ts create mode 100644 src/MiniFlatList/render-window.ts diff --git a/README.md b/README.md index 4226be5..1ac1aef 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,36 @@ area and replaces the rest with spacer views. Item heights are measured with `onLayout` and cached by index; unmeasured items are assumed to be 50 px tall. Expect blank areas and content jumps while scrolling fast. +```tsx +import { MiniFlatList } from 'react-native-tiny-list'; + + item.id} + initialNumToRender={10} + windowSize={21} + maxToRenderPerBatch={10} + updateCellsBatchingPeriod={50} +/> +``` + +`MiniFlatList` adds the core ideas of React Native's `FlatList` on top of +`TinyList`: + +- **Keys.** `keyExtractor` (default: `item.key`, then `item.id`, then the + index) keys both the items and the cached sizes, so sizes survive inserts and + reorders. Unmeasured items count as the average measured size. +- **`initialNumToRender`.** Items rendered before the viewport is measured. +- **`windowSize`.** The area kept rendered, in viewports, centered on the + visible one. +- **Batched rendering.** Scrolling does not re-render the list while the + visible items are already rendered. The area off screen is filled + `maxToRenderPerBatch` items at a time, every `updateCellsBatchingPeriod` ms. + When the visible area is blank, the list renders right away. +- **Memoized items.** Each item re-renders only when its props or `extraData` + change. + ## Contributing diff --git a/example/src/App.tsx b/example/src/App.tsx index 9a3da52..6bc583b 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,5 +1,10 @@ -import { StyleSheet, Text, View } from 'react-native'; -import { TinyList } from 'react-native-tiny-list'; +import { useState } from 'react'; +import { Button, StyleSheet, Text, View } from 'react-native'; +import { + MiniFlatList, + TinyList, + type MiniFlatListRenderItemInfo, +} from 'react-native-tiny-list'; type Item = { id: number; title: string }; @@ -8,18 +13,31 @@ const DATA: Item[] = Array.from({ length: 10_000 }, (_, i) => ({ title: `Item ${i}`, })); +// Defined outside the component so it stays stable and MiniFlatList can skip +// re-rendering unchanged items. +function renderItem({ item, index }: MiniFlatListRenderItemInfo) { + return ( + // Vary heights to exercise measurement. + + {item.title} + + ); +} + export default function App() { + const [list, setList] = useState<'tiny' | 'mini'>('mini'); + return ( - ( - // Vary heights to exercise measurement. - - {item.title} - - )} - /> + +