React Native: Rendering infinite number of pages (bi-directional swiping)
This solution is based on this react-native-swiper
you mentioned.
This is a tricky solution that can work if caching is done properly.
It's based on the following assumptions:
- Given an ID, you can derive ID+1 and ID-1. (This is valid for dates)
- You can scroll left or right only by 1 frame(View).
The idea is to have a wrapper component around the <Swiper>
to hold an state array with 3 IDs: current, left and right.
state = {
pages: ["2", "3", "4"],
key: 1
}
When you swipe left or right, you can derive the next or previous 3 IDs and call setState
. The render
method would call the local cached information to get the view attributes for this 3 IDs and render the new Screens. The <Swiper>
element always have to point to index={1}
as initial param, and keep it this way.
const cache = {
"0": "zero",
"1": "one",
"2": "two",
"3": "three",
"4": "four",
"5": "five",
"6": "six",
"7": "seven",
"8": "eight",
"9": "nine",
"10": "ten"
}
renderItem(item, idx) {
const itemInt = parseInt(item)
const style = itemInt % 2 == 0 ? styles.slide1 : styles.slide2
return (
<View style={style} key={idx}>
<Text style={styles.text}>{cache[item]}</Text>
</View>
)
}
However, when you swipe, it will set the current index to 2 or 0. So in order to make it to always point to index 1 (again, because you shifted and reloaded), you have to force the <Swiper>
element to reload. You can do that by setting its key attribute to be defined by a state variable that always changes. That's the tricky part.
onPageChanged(idx) {
if (idx == 2) {
//deriving ID+1
const newPages = this.state.pages.map(i => (parseInt(i)+1).toString())
this.setState({pages: newPages, key: ((this.state.key+1)%2) })
} else if (idx == 0) {
//deriving ID-1
const newPages = this.state.pages.map(i => (parseInt(i)-1).toString())
this.setState({pages: newPages, key: ((this.state.key+1)%2) })
}
}
render() {
return (
<Swiper
index={1}
key={this.state.key}
style={styles.wrapper}
loop={false}
showsPagination={false}
onIndexChanged={(index) => this.onPageChanged(index)}>
{this.state.pages.map((item, idx) => this.renderItem(item, idx))}
</Swiper>
);
}
Aside all that, you have to make sure to always have enough cached information for your IDs. If the cache access is getting close to end, make sure to request new data and delete some amount of cached data in the other direction.
Here's a PasteBin for the App.js and a GitHub project so you can test the whole idea.