qq1o9u81
qq1o9u81

Reputation: 41

Why is my tRPC + Next 14 (app router) data fetching pattern not refreshing the UI?

Tech stack (create-t3-app): Next 14 (app router), React, tRPC

Goals of this post:

My code:

Client component


export default function Page({ params }: { params: { doc: string } }) {
  const router = useRouter();

  const { data, refetch } = api.docs.getDoc.useQuery({
    docId: parseInt(params.doc),
  });

  const toggleLock = api.docs.toggleLock.useMutation({
    onSuccess: () => {
      router.refresh(); // doesn't actually refresh the UI
      revalidatePath("/"); // doesn't actually refresh the UI
    },
  });

  if (!data) {
    return <div>loading...</div>;
  }

  <Button
    disabled={toggleLock.isLoading}
    variant="secondary"
    className="mt-2"
    onClick={() =>
      toggleLock.mutate({
        docId: parseInt(params.doc),
      })
    }
  >
    {toggleLock.isLoading ? "LOADING..." : "Toggle Lock"}
  </Button>

</div>
    ...

tRPC route:


  toggleLock: publicProcedure
    .input(z.object({ docId: z.number() }))
    .mutation(async ({ ctx, input }) => {


      const docBefore = await ctx.db.query.docs.findFirst({
        where(fields, operators) {
          return operators.eq(fields.id, input.docId)
        },
      })
      const res = await ctx.db.update(docs).set({
        isLocked: docBefore?.isLocked ? false : true,
      }).where(eq(docs.id, input.docId))

      const docAfter = await ctx.db.query.docs.findFirst({
        where(fields, operators) {
          return operators.eq(fields.id, input.docId)
        },
      })

      return docAfter;
    }),

As you can see, the workflow is:

  1. make a mutation on FE that toggles the docs.isLocked column
  2. fetch the row on the backend to get the current state of said column
  3. toggle mutation
  4. fetch it after to get new state (mySql doesn't do returning updates)
  5. refresh the UI (CURRENTLY NOT WORKING + DONT HAVE SOLUTION)

My questions

  1. Why is my UI not updating? router.refresh and revalidatepath do nothing
  2. What is the best datA fetching/mutating pattern to run this toggle flow?

Upvotes: 1

Views: 1118

Answers (1)

Yann Vo
Yann Vo

Reputation: 1971

My understanding is that:

  • revalidatePath is only meant to deal with caching and only works in a "server action" (see below)
  • router.refresh() both clears the client (router) cache and make a new request to the server for the current route... but unlike a page refresh (window.location.reload()) I don't believe that it will reset the state of all components!

https://nextjs.org/docs/app/building-your-application/caching

Back to your problem, your code excerpt how do you render what is supposed to be refreshed? I suppose that your are using the getDoc tRPC value.

In that case, you simply need to invalidate that.

https://trpc.io/docs/client/react/useUtils#invalidating-a-single-query

export default function Page({ params }: { params: { doc: string } }) {
  const utils = api.useUtils();

  const { data, refetch } = api.docs.getDoc.useQuery({
    docId: parseInt(params.doc),
  });

  const toggleLock = api.docs.toggleLock.useMutation({
    onSuccess: () => {
       utils.docs.getDoc.invalidate({
         docId: parseInt(params.doc),
       });
    },
  });

Note on revalidatePath

revalidatePath will be useful once you start navigating from page to page... so that you do not show stale data. Unfortunately, calling it from a tRPC route handler does not work. I only managed to make it work when called from a server action.

Upvotes: 0

Related Questions