oscklm
oscklm16mo ago

Confused with passing additional arguments to paginated query

Hello, So i'm trying to play around with passing paginated messages, and when i pass additional arguments to the query, it breaks something.
// convex/messages.ts
export const all = query({
args: { paginationOpts: paginationOptsValidator, channelId: v.string() },
handler: async (ctx, args) => {
const channelId = ctx.db.normalizeId('channels', args.channelId)

if (!channelId) return
// Grab the most recent messages.
const result = await ctx.db
.query('messages')
.withIndex('by_channel', (q) => q.eq('channelId', channelId))
.order('desc')
.paginate(args.paginationOpts)

return {
...result,
page: (await enrichMessages(ctx, result.page)).reverse(),
}
},
})

async function enrichMessages(ctx: QueryCtx, messages: Doc<'messages'>[]) {
return await async.map(messages, (message) => enrichMessage(ctx, message))
}

async function enrichMessage(ctx: QueryCtx, message: Doc<'messages'>) {
const author = await ctx.db.get(message.authorId)
if (author === null) {
return null
}
return { ...message, author }
}
// convex/messages.ts
export const all = query({
args: { paginationOpts: paginationOptsValidator, channelId: v.string() },
handler: async (ctx, args) => {
const channelId = ctx.db.normalizeId('channels', args.channelId)

if (!channelId) return
// Grab the most recent messages.
const result = await ctx.db
.query('messages')
.withIndex('by_channel', (q) => q.eq('channelId', channelId))
.order('desc')
.paginate(args.paginationOpts)

return {
...result,
page: (await enrichMessages(ctx, result.page)).reverse(),
}
},
})

async function enrichMessages(ctx: QueryCtx, messages: Doc<'messages'>[]) {
return await async.map(messages, (message) => enrichMessage(ctx, message))
}

async function enrichMessage(ctx: QueryCtx, message: Doc<'messages'>) {
const author = await ctx.db.get(message.authorId)
if (author === null) {
return null
}
return { ...message, author }
}
// message-list.tsx
const MessageList: FC<MessageListProps> = ({ channelId }) => {
const {
results: messages,
} = usePaginatedQuery(
api.messages.all,
{ channelId },
{ initialNumItems: 25 }
)
if (!messages) return <div>Loading...</div>

return (
<div className="space-y-2">
{messages.map((message) => {
if (!message) return null
return (
<div key={message?._id} className="flex items-center space-x-1 p-2">
<h2>{message.author.name}</h2>
<small className="">
{convertUnixToTimestamp(message._creationTime)}
</small>
<p>{message.body}</p>
</div>
)
})}
</div>
)
}
// message-list.tsx
const MessageList: FC<MessageListProps> = ({ channelId }) => {
const {
results: messages,
} = usePaginatedQuery(
api.messages.all,
{ channelId },
{ initialNumItems: 25 }
)
if (!messages) return <div>Loading...</div>

return (
<div className="space-y-2">
{messages.map((message) => {
if (!message) return null
return (
<div key={message?._id} className="flex items-center space-x-1 p-2">
<h2>{message.author.name}</h2>
<small className="">
{convertUnixToTimestamp(message._creationTime)}
</small>
<p>{message.body}</p>
</div>
)
})}
</div>
)
}
I'm only getting this error, and loosing my types - but other than that i get the messages with the authors and can display them as expected.
No description
7 Replies
oscklm
oscklmOP16mo ago
I just tried backtracking a bit, and seems like the issues lies in the withIndex, because i only want the messages that has the correct channelId If i remove withIndex, everything flows through without issues. I cant seem to put my finger on why its breaking the query reference, atleast i think thats whats up
ballingt
ballingt16mo ago
This is because of the if (!channelId) return; line: this query can return undefined (translated to null on the client), which doesn't fit the paginated query signature
oscklm
oscklmOP16mo ago
Ohh darn it, is that it haha :noice:
ballingt
ballingt16mo ago
I'd use "skip" on the client to avoid this... oh right, we haven't published skip for usePaginatedQuery yet! It's coming! I think you you can use a nonexistent channel id by just removing that line should return zero results
oscklm
oscklmOP16mo ago
Yeah was about to say that, it's prob not end of the world removing that check. Thanks for clearing it up, i def ended up overthinking what was happening here
ballingt
ballingt16mo ago
You could also move this usePaginatedQuery hook into a component that is only rendered if you have a channelId, we've talked about this workaround before so you may be tired of hearing about it when "skip" would be more convenient. Don't worry, it's coming! Thanks for posting your code, made this much easier to figure out!
oscklm
oscklmOP16mo ago
Your most welcome. I think the reason i put the check there in the first place, was that .withIndex('by_channel', (q) => q.eq('channelId', channelId)) will complain that channelId cant be null And its required to be a Id<"channels"> If i just do
.withIndex('by_channel', (q) =>
q.eq('channelId', channelId || ('' as Id<'channels'>))
)
.withIndex('by_channel', (q) =>
q.eq('channelId', channelId || ('' as Id<'channels'>))
)
That works, but results in the usePaginatedQuery isLoading just ends up returning true forever. But i'm asuming there is no way around this for now?

Did you find this page helpful?