Baboluo
Baboluo14mo ago

How to do an async API call in useEffect (T3 stack)

Hey, I have the router below and want to call the tutorasync in an useCallback function, but the only method that is available is api.chat.queryTutor.useQuery() which is a hook where you add the input in advance. But I have it available only in the useCallbackfunction. is there a way to access the function directly without a hook?
export const chatRouter = createTRPCRouter({
tutor: publicProcedure
.input(
z.object({
messages: z.array(
z.object({
id: z.string(),
text: z.string(),
role: z.enum(["user", "system", "assistant"]),
addToPrompt: z.boolean(),
})
),
})
)
.query(async ({ input }): Promise<ChatMessage> => {
// ...
return {
// ...
}
}),
})
export const chatRouter = createTRPCRouter({
tutor: publicProcedure
.input(
z.object({
messages: z.array(
z.object({
id: z.string(),
text: z.string(),
role: z.enum(["user", "system", "assistant"]),
addToPrompt: z.boolean(),
})
),
})
)
.query(async ({ input }): Promise<ChatMessage> => {
// ...
return {
// ...
}
}),
})
Solution:
You are after all getting a new state back from the request, the backend is creating something for you even if it doesn't store it
Jump to solution
9 Replies
Nick
Nick14mo ago
You might want a mutation instead?
Baboluo
Baboluo14mo ago
Unlike queries, mutations are typically used to create/update/delete data or perform server side-effects. from the docs. I don't do CRUD ops or perform server side-effects. just accessing a 3rd party API based on parameters from the user interface so mutation doesnt make sense intuitively
Nick
Nick14mo ago
Sure, then using the callback to set some state which drives the query is probably the right way Calling a query imperatively is a code smell (though not necessarily wrong 100% of the time) and indicates your mindset is wrong
Baboluo
Baboluo14mo ago
Idk using state as means to drive useQuery is kinda opaque and unintuitive You think this is bad?
const [chatMessages, setChatMessages] = useRecoilState(chatMessagesState)
const [textareaText, setTextareaText] = useState("")
const [chatLoading, setChatLoading] = useState(false)
const ctx = api.useContext()

const sendUserMessage = useCallback(async () => {
if (textareaText === "") return
if (chatLoading) return

const currentMessages = [...chatMessages]
currentMessages.push({
id: uuidv4(),
role: "user",
text: textareaText,
addToPrompt: true,
})

setChatMessages(currentMessages)

setTextareaText("")
setChatLoading(true)

const answerMessage = await ctx.chat.queryTutor.fetch({
messages: currentMessages,
})
const messagesWithAnswer = [...currentMessages, answerMessage]

setChatMessages(messagesWithAnswer)
setChatLoading(false)
}, [
// ...
])
const [chatMessages, setChatMessages] = useRecoilState(chatMessagesState)
const [textareaText, setTextareaText] = useState("")
const [chatLoading, setChatLoading] = useState(false)
const ctx = api.useContext()

const sendUserMessage = useCallback(async () => {
if (textareaText === "") return
if (chatLoading) return

const currentMessages = [...chatMessages]
currentMessages.push({
id: uuidv4(),
role: "user",
text: textareaText,
addToPrompt: true,
})

setChatMessages(currentMessages)

setTextareaText("")
setChatLoading(true)

const answerMessage = await ctx.chat.queryTutor.fetch({
messages: currentMessages,
})
const messagesWithAnswer = [...currentMessages, answerMessage]

setChatMessages(messagesWithAnswer)
setChatLoading(false)
}, [
// ...
])
Nick
Nick14mo ago
I think there are some structural issues in both your API and client, yes useQuery() should just fetch all the messages, and you can use optimistic updates to append the new message, then invalidate the cache so it refetches
Nick
Nick14mo ago
Optimistic Updates | TanStack Query Docs
When you optimistically update your state before performing a mutation, there is a chance that the mutation will fail. In most of these failure cases, you can just trigger a refetch for your optimistic queries to revert them to their true server state. In some circumstances though, refetching may not work correctly and the mutation error could ...
Baboluo
Baboluo14mo ago
there's no server side state, its client side. the backend is an wraps an api that yields results of a machine learning model. thanks so far though, I don't want to waste your time so I'll just stick with this for now
Nick
Nick14mo ago
Got it, in that case I'd say this is a mutation
Solution
Nick
Nick14mo ago
You are after all getting a new state back from the request, the backend is creating something for you even if it doesn't store it