allen
allen2y ago

Paginated query briefly returning empty array

I'm observing a defective behavior where a newly mounted paginated query briefly returns an empty array before returning a result set. The expected behavior is that it returns undefined until results are ready. This results in my app rendering an empty state view instead of a loading view while this query is in flight. There is some async linked document population that occurs, but I'm unsure why that would impact the query resolution. The basic design of my query is as follows:
export default query(
async ({ db }, opts) => {
const results = await db
.query('messages')
.paginate(opts);

const page = await Promise.all(
results.page.map(async (item) => {
return {
...item,
focus: (await db.get(item.focusId)) as Doc<'focuses'>,
fromUser: (await db.get(item.fromUserId)) as Doc<'users'>,
};
}),
);

return {
isDone: results.isDone,
continueCursor: results.continueCursor,
page,
};
},
);
export default query(
async ({ db }, opts) => {
const results = await db
.query('messages')
.paginate(opts);

const page = await Promise.all(
results.page.map(async (item) => {
return {
...item,
focus: (await db.get(item.focusId)) as Doc<'focuses'>,
fromUser: (await db.get(item.fromUserId)) as Doc<'users'>,
};
}),
);

return {
isDone: results.isDone,
continueCursor: results.continueCursor,
page,
};
},
);
4 Replies
allen
allenOP2y ago
As I look closer, I'm concerned that the map isnt awaiting properly. Trying a refactor Hmm even with a different approach, I'm seeing the same behavior. If I log out the result of the query on the client, it comes out like this during app start:
LOG {"loadMore": undefined, "results": [], "status": "LoadingMore"}
LOG {"loadMore": undefined, "results": [], "status": "LoadingMore"}
LOG {"loadMore": undefined, "results": [], "status": "LoadingMore"}
LOG {"loadMore": undefined, "results": [], "status": "LoadingMore"}
LOG {"loadMore": undefined, "results": [{"_creationTime": 1680211055874.1865, "_id": [_Id],
LOG {"loadMore": undefined, "results": [], "status": "LoadingMore"}
LOG {"loadMore": undefined, "results": [], "status": "LoadingMore"}
LOG {"loadMore": undefined, "results": [], "status": "LoadingMore"}
LOG {"loadMore": undefined, "results": [], "status": "LoadingMore"}
LOG {"loadMore": undefined, "results": [{"_creationTime": 1680211055874.1865, "_id": [_Id],
Its never in an undefined state and it starts in a status of LoadingMore with an empty result set. Seems like there is a bug here. just reviewed the docs, and perhaps this is expected behavior. Just caught me off guard with the difference between this and normal queries. Also, I wouldnt expect the initial status to be LoadingMore as I havent invoked that function at this point. I also don't want my view to go back to a loading state with fetching more results. I may be able to lean on a composite key between loadMore and status to shake this out, but something more explicit, and preferablly something that matches the behavior of useQuery would be preferable. For clarity, my expectation was that results would be undefined during the initial fetch, as this is the behavior of useQuery
alexcole
alexcole2y ago
Yeah, this is the expected behavior, but I agree that it's a little confusing. And just to make sure I understand, you want the view to have some initial loading state that is separate from the UI when it is loading more items? Yeah, you can achieve this by looking at the length of results and the status, but it's janky. I designed it this way because often "infinite scroll" paginated views will having the same loading indicator when loading the first page as they do when they are loading later pages. But perhaps this isn't the right default.
allen
allenOP2y ago
And just to make sure I understand, you want the view to have some initial loading state that is separate from the UI when it is loading more items?
yes
looking at the length
Thats where I landed, but theres some edge cases I can imagine
infinite scroll
Its more of the empty state i'm concerned with "empty state" meaning post first load and still empty result set. It would be cleaner to have the results be undefined prior to first load (like useQuery) or a distinct status that I can discriminate from LoadingMore
ian
ian2y ago
Somewhat related, Anjana put together a post on limiting the flickering when going back to a loading state, by accepting stale data while it's loading in this post: https://stack.convex.dev/help-my-app-is-overreacting One thing that I believe is true, is the results will never be empty with LoadingMore set after the first results come back. If there is no data to load, results will be [] and the status will be "Exhausted". Agreed the API is different here, which is confusing
Help, my app is overreacting!
Reactive backends like Convex make building live-updating apps a cinch, but default behavior might be too reactive for some use cases. Not to worry! L...

Did you find this page helpful?