Add toggle to switch between list impl, add default keyExtractor

This commit is contained in:
Eric Bailey
2025-08-27 20:57:54 -05:00
parent d53c0e548f
commit afa6d67c23
2 changed files with 67 additions and 35 deletions
+15 -19
View File
@@ -26,7 +26,10 @@ export {
*
* - `contentOffset` - Use `headerOffset` or `footerOffset`
*/
type ListProps<Item> = Omit<FlatListProps<Item>, 'contentOffset'> & {
type ListProps<Item extends {key: string}> = Omit<
FlatListProps<Item>,
'contentOffset'
> & {
/**
* Wrapper around `onViewableItemsChanged` that calls back with individual
* items IF they `item.isViewable` is true.
@@ -49,7 +52,7 @@ type ListProps<Item> = Omit<FlatListProps<Item>, 'contentOffset'> & {
onScrolledDownChange?: (isScrolledDown: boolean) => void
}
export const List = forwardRef(function List<Item>(
export const List = forwardRef(function List<Item extends {key: string}>(
props: ListProps<Item>,
ref: React.Ref<FlatList<Item>>,
) {
@@ -126,6 +129,7 @@ export const List = forwardRef(function List<Item>(
return (
<Animated.FlatList
ref={ref}
keyExtractor={props.keyExtractor || (i => i.key)}
viewabilityConfig={viewabilityConfig}
onViewableItemsChanged={onViewableItemsChanged}
/**
@@ -150,14 +154,6 @@ export const List = forwardRef(function List<Item>(
*/
right: 1,
}}
/**
* iOS-only, should match `scrollIndicatorInsets`
* @see https://reactnative.dev/docs/scrollview#contentinset-ios
*/
contentInset={{
top: props.headerOffset ?? 0,
bottom: props.footerOffset ?? 0,
}}
/**
* Native only. On web, we use padding on `style` instead.
*/
@@ -168,21 +164,21 @@ export const List = forwardRef(function List<Item>(
refreshControl={refreshControl}
{...(props as FlatListPropsWithLayout<Item>)}
style={[
/*
* On web, the List should always fill its container, otherwise
* `onScroll` will not work due to the entire page scrolling.
*/
{
paddingTop: props.headerOffset,
paddingBottom: props.footerOffset,
},
web([
/*
* On web, the List should always fill its container, otherwise
* `onScroll` will not work due to the entire page scrolling.
*/
a.h_full,
{
paddingTop: props.headerOffset,
paddingBottom: props.footerOffset,
},
]),
]}
onScroll={onScroll}
/>
)
}) as <Item>(
}) as <Item extends {key: string}>(
props: ListProps<Item> & {ref?: React.Ref<FlatList<Item>>},
) => React.ReactElement
+52 -16
View File
@@ -1,7 +1,10 @@
import {useState} from 'react'
import {View} from 'react-native'
import {runOnJS} from 'react-native-reanimated'
import {List as OldList} from '#/view/com/util/List'
import {atoms as a} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import * as Layout from '#/components/Layout'
import {List, ListScrollProvider, useListScrollHandler} from '#/components/List'
import {Text} from '#/components/Typography'
@@ -36,6 +39,7 @@ type Item = {
const log = (msg: any) => console.log(msg)
export function Inner() {
const [old, setOld] = useState<boolean>(false)
const onScrollWorklet = useListScrollHandler(e => {
'worklet'
runOnJS(log)(`scroll ${e.contentOffset.y}`)
@@ -43,22 +47,54 @@ export function Inner() {
return (
<View style={[a.h_full_vh]}>
<ListScrollProvider onScroll={onScrollWorklet}>
<List<Item>
data={items}
headerOffset={100}
footerOffset={100}
onScrolledDownChange={scrolledDown => {
console.log(`Scrolled down: ${scrolledDown}`)
}}
renderItem={({item}) => (
<View style={[a.p_md, a.border_b]}>
<Text>{item.title}</Text>
</View>
)}
style={[a.debug]}
/>
</ListScrollProvider>
<View
style={[a.fixed, a.w_full, a.py_sm, a.px_xl, a.align_center, a.z_10]}>
<Button
label="Toggle Old/New List"
onPress={() => setOld(v => !v)}
size="tiny"
color="primary_subtle">
<ButtonText>
{old ? 'Testing old List' : 'Testing new List'}
</ButtonText>
</Button>
</View>
{!old ? (
<ListScrollProvider onScroll={onScrollWorklet}>
<List<Item>
data={items}
headerOffset={100}
footerOffset={100}
onScrolledDownChange={scrolledDown => {
console.log(`Scrolled down: ${scrolledDown}`)
}}
renderItem={({item}) => (
<View style={[a.p_md, a.border_b]}>
<Text>{item.title}</Text>
</View>
)}
style={[a.debug]}
/>
</ListScrollProvider>
) : (
<ListScrollProvider onScroll={onScrollWorklet}>
<OldList
data={items}
keyExtractor={item => item.key}
headerOffset={100}
onScrolledDownChange={scrolledDown => {
console.log(`Scrolled down: ${scrolledDown}`)
}}
renderItem={({item}) => (
<View style={[a.p_md, a.border_b]}>
<Text>{item.title}</Text>
</View>
)}
style={[a.debug]}
/>
</ListScrollProvider>
)}
</View>
)
}