-
-
Save adriancbo/2568e80479276bd90b277fae212d04c3 to your computer and use it in GitHub Desktop.
Infinite Scrolling List Flavors
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState, useEffect } from 'react' | |
import { | |
SafeAreaView, | |
View, | |
FlatList, | |
StyleSheet, | |
Text, | |
Dimensions | |
} from 'react-native' | |
const useInfiniteScroll = load => { | |
const [isFetching, setIsFetching] = useState(true) | |
const [data, setData] = useState([]) | |
useEffect(() => { | |
let didCancel = false | |
if (!isFetching) return | |
const loadAsync = async () => { | |
const lastIndex = data.length | |
const lastItem = data.length ? data[lastIndex] : null | |
const newData = await load({ lastIndex, lastItem }) | |
if (!didCancel) { | |
setData(prevState => [...prevState, ...newData]) | |
setIsFetching(false) | |
} | |
} | |
loadAsync() | |
return () => { | |
didCancel = true | |
} | |
}, [isFetching]) | |
return [data, isFetching, setIsFetching] | |
} | |
const INITIAL_LOAD = 30 | |
const PAGE_SIZE = 20 | |
export default () => { | |
/** | |
* Right now, I'm mandating that whatever this method is accepts as a | |
* parameter an object containing the objects `lastIndex` and `lastObject` | |
* respectively. I believe this should suffice for effective paging. | |
* | |
* @param lastIndex | |
* @returns {Promise<R>} | |
*/ | |
const fetchMoreListItems = ({ lastIndex }) => { | |
// Simulate fetch of next 20 items (30 if initial load) | |
return new Promise(resolve => { | |
setTimeout(() => { | |
resolve([ | |
...Array.from( | |
Array(lastIndex === 0 ? INITIAL_LOAD : PAGE_SIZE).keys(), | |
n => { | |
n = n + lastIndex | |
return { | |
number: n.toString(), | |
id: n.toString() | |
} | |
} | |
) | |
]) | |
}, 2000) | |
}) | |
} | |
const [data, isFetching, setIsFetching] = useInfiniteScroll( | |
fetchMoreListItems | |
) | |
return ( | |
<SafeAreaView style={styles.container}> | |
<View style={styles.blueBox}> | |
<Text style={styles.bigWhiteBoldText}> | |
{`${data.length} Items Loaded`} | |
</Text> | |
</View> | |
<FlatList | |
onEndReachedThreshold={7} | |
onEndReached={() => { | |
if (!isFetching) { | |
setIsFetching(true) | |
} | |
}} | |
data={data} | |
keyExtractor={item => item.id} | |
renderItem={({ item }) => { | |
return <Item item={item} /> | |
}} | |
/> | |
{isFetching && ( | |
<View style={styles.blueBox}> | |
<Text style={styles.bigWhiteBoldText}>(Fetching More)</Text> | |
</View> | |
)} | |
</SafeAreaView> | |
) | |
} | |
class Item extends React.PureComponent { | |
render() { | |
return ( | |
<View style={styles.item}> | |
<Text style={styles.title}>{this.props.item.number}</Text> | |
</View> | |
) | |
} | |
} | |
const styles = StyleSheet.create({ | |
container: { | |
flex: 1, | |
marginTop: 24, | |
backgroundColor: 'yellow' | |
}, | |
item: { | |
backgroundColor: '#f9c2ff', | |
alignItems: 'center', | |
justifyContent: 'center', | |
height: Dimensions.get('window').height * 0.45, | |
marginVertical: 8, | |
marginHorizontal: 16 | |
}, | |
title: { | |
fontSize: 48 | |
}, | |
blueBox: { | |
height: 50, | |
backgroundColor: 'blue', | |
justifyContent: 'center', | |
alignItems: 'center' | |
}, | |
bigWhiteBoldText: { | |
color: 'white', | |
fontSize: 32, | |
fontWeight: 'bold' | |
} | |
}) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, {useState, useRef, useMemo} from 'react'; | |
import { | |
SafeAreaView, | |
TouchableOpacity, | |
FlatList, | |
StyleSheet, | |
Text, | |
} from 'react-native'; | |
const DATA = [ | |
{ | |
id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba', | |
title: 'First Item', | |
selected: false, | |
}, | |
{ | |
id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63', | |
title: 'Second Item', | |
selected: false, | |
}, | |
{ | |
id: '58694a0f-3da1-471f-bd96-145571e29d72', | |
title: 'Third Item', | |
selected: false, | |
}, | |
]; | |
function Item({item: {id, title, selected}, onSelect}) { | |
console.log('L29 "item is rendering" ===', id); | |
return ( | |
<TouchableOpacity | |
onPress={() => onSelect(id)} | |
style={[ | |
styles.item, | |
{backgroundColor: selected ? '#6e3b6e' : '#f9c2ff'}, | |
]}> | |
<Text style={styles.title}>{title}</Text> | |
</TouchableOpacity> | |
); | |
} | |
function ItemPureFunctional({item: {id, title, selected}, onSelect}) { | |
return useMemo(() => { | |
console.log('L44 "item is rendering" ===', id); | |
return ( | |
<TouchableOpacity | |
onPress={() => onSelect(id)} | |
style={[ | |
styles.item, | |
{backgroundColor: selected ? '#6e3b6e' : '#f9c2ff'}, | |
]}> | |
<Text style={styles.title}>{title}</Text> | |
</TouchableOpacity> | |
); | |
}, [selected]); | |
} | |
class ItemPureComponent extends React.PureComponent { | |
render() { | |
return ( | |
<TouchableOpacity | |
onPress={() => this.props.onSelect(this.props.id)} | |
style={[ | |
styles.item, | |
{backgroundColor: this.props.selected ? '#6e3b6e' : '#f9c2ff'}, | |
]}> | |
<Text style={styles.title}>{this.props.title}</Text> | |
</TouchableOpacity> | |
); | |
} | |
} | |
export default function App() { | |
const [data, setData] = useState(DATA); | |
const onSelect = useRef(id => { | |
setData(oldData => { | |
return [ | |
...oldData.map(item => { | |
if (id === item.id) { | |
return { | |
...item, | |
selected: !item.selected, | |
}; | |
} | |
return item; | |
}), | |
]; | |
}); | |
}); | |
return ( | |
<SafeAreaView style={styles.container}> | |
<FlatList | |
data={data} | |
renderItem={({item}) => ( | |
<ItemPureFunctional item={item} onSelect={onSelect.current} /> | |
)} | |
keyExtractor={item => item.id} | |
/> | |
</SafeAreaView> | |
); | |
} | |
const styles = StyleSheet.create({ | |
container: { | |
flex: 1, | |
marginTop: 24, | |
}, | |
item: { | |
backgroundColor: '#f9c2ff', | |
padding: 20, | |
marginVertical: 8, | |
marginHorizontal: 16, | |
}, | |
title: { | |
fontSize: 32, | |
}, | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment