Reputation: 1072
I'm implementing a pagination where the user clicks a button to load more items. I'm following the docs at https://firebase.google.com/docs/firestore/query-data/query-cursors (paginate a query)
I'm passing in the last item id to the startAfter() function. This should make the next query return items that comes after the passed in item id.
What actually is happening, is that the previous items get loaded again. So if the initial fetches looks like:
[
0: {id: "1", stuff: ...}
1: {id: "2", stuff: ...}
2: {id: "3", stuff: ...}
]
the id 3 will get passed into startAfter(), and should load
[
0: {id: "4", stuff: ...}
1: {id: "5", stuff: ...}
]
instead, it loads the initial items
[
0: {id: "1", stuff: ...}
1: {id: "2", stuff: ...}
]
So I end up displaying duplicate items when I press my 'load more' button.
initial fetch function:
const [recipes, setRecipes] = useState([]);
useEffect(() => {
const fetchData = async () => {
const obj = [];
const firstFetch = await db
.collection('recipes')
.orderBy('createdAt', 'desc')
.limit(3)
.get();
const snapshot = await firstFetch;
snapshot.forEach((doc) => {
obj.push({ id: doc.id, ...doc.data() });
});
const allRecipes = {
userRecipes: obj,
};
setRecipes(allRecipes.userRecipes);
};
fetchData();
}, []);
The recipe variable logs:
[
0: {id: "1", stuff: ...}
1: {id: "2", stuff: ...}
2: {id: "3", stuff: ...}
]
The button to load more items:
<button
type="button"
onClick={() => showMore({ recipe: recipes[recipes.length - 1] })}
>
Show More
</button>
Which calls the showMore function:
const showMore = async ({ recipe }) => {
const data = await db
.collection('recipes')
.orderBy('createdAt', 'desc')
.startAfter(recipe)
.limit(2)
.get();
const moreData = data.docs.map((doc) => ({ id: doc.id, ...doc.data() }));
setRecipes([...recipes, ...moreData]);
};
If I log the 'moreData' variable:
[
0: {id: "1", stuff: ...}
1: {id: "2", stuff: ...}
]
if I log the updated 'recipe' variable:
[
0: {id: "1", stuff: ...}
1: {id: "2", stuff: ...}
2: {id: "3", stuff: ...}
0: {id: "1", stuff: ...}
1: {id: "2", stuff: ...}
]
So to me, it seems like it's ignoring the startAfter() function, and loading everything from the start instead.
Any pointers on what am I doing wrong?
Edit: The actual data in the db looks like this:
{
about: "Non magnam quasi tempore."
createdAt: t {seconds: 1596741068, nanoseconds: 531000000}
description: "Ea quibusdam odit quasi corporis et nostrum."
imageUrl: "http://lorempixel.com/640/480/food"
ingredients: [{…}]
likes: []
title: "Ella"
userId: "U8n09jb0ZENwrZhDeYrJUSikcM12"
userSaves: ["uM7A4r5O6ENiX3qTL4tGhDXYA3Z2"]
username: "foobar"
}
the ID is the name of the document. The ID gets appended to the document in the initial fetch function.
SOLUTION:
Looking at Frank van Puffelen's answer, I had to provide the complete snapshot (which includes all info about the item), not only the array with items in it.
By creating a new hook with useState, I could store the state of the last snapshot in it:
const last = snapshot.docs[snapshot.docs.length - 1]
setLastDocSnap(last)
in my fetchMore() function, I was able to se the state in the startAfter() function:
const data = await db
.collection('recipes')
.orderBy('createdAt')
.limit(limit)
.startAfter(lastDocSnap)
.get();
Upvotes: 0
Views: 1153
Reputation: 598797
I am not able to reproduce the problem. I've simplified your code to:
ref.orderBy('id')
.limit(3)
.get()
.then(function(querySnapshot) {
console.log("Initial query: "+querySnapshot.docs.length+" results");
querySnapshot.forEach(function(doc) {
console.log(doc.data().id);
})
var last = querySnapshot.docs[querySnapshot.docs.length - 1];
ref.orderBy('id')
.startAfter(last)
.limit(3)
.get()
.then(function(querySnapshot) {
console.log("Second query: "+querySnapshot.docs.length+" results");
querySnapshot.forEach(function(doc) {
console.log(doc.data().id);
})
});
});
And then run it on a collection with 5 documents, with id
values 1
through 5
. It's output is:
Initial query: 3 results
1
2
3
Second query: 2 results
4
5
Which is as I'd expect it to be.
You can see the running code here: https://jsbin.com/qusijaw/edit?js,console
If my results don't help you get it working on your end, can you try to reproduce the problem in an environment like jsbin, so that we can take a look at it?
Upvotes: 1