carnegiepilled
carnegiepilled6mo ago

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

Goals of this post: - to know why my UI is not refreshing after i mutate the backend - to learn a better data mutation and update pattern 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>
...
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;
}),
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 route (not working) My questions 1. Why is my UI not updating? router.refresh and revalidatepath do nothing
2 Replies
Spark
Spark6mo ago
I would take a look at the create-t3-turbo repo and study the data fetching patterns. https://github.com/t3-oss/create-t3-turbo/blob/main/apps/nextjs/src/app/page.tsx Thoughts: 1. not sure if always correct, but I like to keep my pages as server components and import client components if needed 2. to revalidate you can use this https://trpc.io/docs/client/react/useUtils#query-invalidation
GitHub
create-t3-turbo/apps/nextjs/src/app/page.tsx at main · t3-oss/creat...
Clean and simple starter repo using the T3 Stack along with Expo React Native - t3-oss/create-t3-turbo
useUtils | tRPC
useUtils is a hook that gives you access to helpers that let you manage the cached data of the queries you execute via @trpc/react-query. These helpers are actually thin wrappers around @tanstack/react-query's queryClient methods. If you want more in-depth information about options and usage patterns for useContext helpers than what we provide h...
carnegiepilled
carnegiepilled6mo ago
thanks!