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

BBaboluo4/27/2023
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 {
        // ...
      }
    }),
})
Nnlucas4/27/2023
You might want a mutation instead?
BBaboluo4/27/2023
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
BBaboluo4/27/2023
so mutation doesnt make sense intuitively
Nnlucas4/27/2023
Sure, then using the callback to set some state which drives the query is probably the right way
Nnlucas4/27/2023
Calling a query imperatively is a code smell (though not necessarily wrong 100% of the time) and indicates your mindset is wrong
BBaboluo4/27/2023
Idk using state as means to drive useQuery is kinda opaque and unintuitive
BBaboluo4/27/2023
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)
  }, [
    // ...
  ])
Nnlucas4/27/2023
I think there are some structural issues in both your API and client, yes
Nnlucas4/27/2023
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
BBaboluo4/27/2023
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
Nnlucas4/27/2023
Got it, in that case I'd say this is a mutation
Solution
Nnlucas4/27/2023
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