I am displaying “global posts” on one of my tabs. Currently, there are only 11 posts in the database:
In the app Some of the posts are being duplicated, and I have no idea why these SPECIFIC posts are being duplicated, as it seems to me like it is happening at random.
Here is the code for how I paginate the data.
- When the component mounts, I query firestore and pull 5 posts using getCollection().
.
async componentDidMount() { this.unsubscribe = Firebase.firestore() .collection('globalPosts') .orderBy("date_created", "desc") .limit(5) .onSnapshot(this.getCollection); }
- I get the posts successfully in getCollection(), and set an index, lastItemIndex, so I know where to query for the next posts
.
getCollection = (querySnapshot) => { const globalPostsArray = []; querySnapshot.forEach((res) => { const { ..fields } = res.data(); globalPostsArray.push({ ..fields }); }); this.setState({ globalPostsArray, isLoading: false, lastItemIndex: globalPostsArray.length - 1 }); }
- This gets the first 5 items, no problem, ordered by date_created, descending.
If the user scrolls down the flatlist, I have logic in the flatlist to handle fetching more data:
<FlatList data={this.state.globalPostsArray} renderItem={renderItem} keyExtractor={item => item.key} contentContainerStyle={{ paddingBottom: 50 }} showsHorizontalScrollIndicator={false} showsVerticalScrollIndicator={false} onRefresh={this._refresh} refreshing={this.state.isLoading} onEndReachedThreshold={0.5} <---------------------- Threshold onEndReached={() => {this.getMore()}} <------------ Get more data />
- Finally, once it is time to retrieve more data, I call this.getMore()
Here is the code to get the next 5 posts:
getMore = async() => { const newPostsArray = [] <-------- new array for the next 5 posts Firebase.firestore() .collection('globalPosts') .orderBy("date_created", "desc") .startAfter(this.state.globalPostsArray[this.state.lastItemIndex].date_created) <--- note start after .limit(5) .onSnapshot(querySnapshot => { querySnapshot.forEach((res) => { const { ... same fields as getCollection() } = res.data(); newPostsArray.push({ ... same fields as getCollection() }); }); this.setState({ globalPostsArray: this.state.globalPostsArray.concat(newPostsArray), <--- add to state array lastItemIndex: this.state.globalPostsArray.length-1 <---- increment index }); console.log(this.state.lastItemIndex) <------- I print out last item index }) }
Some notes:
The code works fine in terms of fetching the data
The code works fine in terms of pagination, and only fetches 5 posts at a time
There is no discernible pattern I am seeing in which posts are being duplicated
I am ordering by date_created, descending when querying firestore in both getCollection() and getMore()
I console log “last item index” in my getMore(), and of course the index is higher than the number of posts
I keep getting the following warning/error, with different keys (post ID’s in firestore), which shows me the duplication is happening at random, and not specific to one user. This warning/error doesn’t break the application, but is telling me this weird behavior is happening:
Encountered two children with the same key,
ZJu3FbhzOkXDM5mn6O6T
. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
Can someone point me in the right direction, why my pagination is having such unusual behavior?
Advertisement
Answer
My issue was with lastItemIndex
. Saving it in state was causing problems. I solved the problem by removing lastItemIndex
from state, and making it a local variable in getMore():
getMore = async() => { const newPostsArray = [] const lastItemIndex = this.state.globalPostsArray.length - 1 <---- added it here await Firebase.firestore() .collection('globalPosts') .orderBy("date_created", "desc") .startAfter(this.state.globalPostsArray[lastItemIndex].date_created) .limit(5) .onSnapshot(querySnapshot => { querySnapshot.forEach((res) => { const { ..fields } = res.data(); newPostsArray.push({ key: res.id, ..fields }); }); this.setState({ globalPostsArray: this.state.globalPostsArray.concat(newPostsArray) }); }) }