Calling trpc mutations on client components with app router

Hello, looking for some help learning app router, any advice greatly appreciated I'm getting an error trying to call a mutation from a client component - my aim is to collect some values with a form in a shadcn dialog (so useState required) Setting up the mutations for create and edit:
import { api } from "~/trpc/react";
export function EpisodeForm({ id }: EpisodeFormProps) {
const [open, setOpen] = useState(false);

const utils = api.useUtils();
const createEpisode = api.episode.create.useMutation({
onSuccess: () => utils.episode.invalidate(),
});
const updateEpisode = api.episode.update.useMutation({
onSuccess: () => utils.episode.invalidate(),
});
...
import { api } from "~/trpc/react";
export function EpisodeForm({ id }: EpisodeFormProps) {
const [open, setOpen] = useState(false);

const utils = api.useUtils();
const createEpisode = api.episode.create.useMutation({
onSuccess: () => utils.episode.invalidate(),
});
const updateEpisode = api.episode.update.useMutation({
onSuccess: () => utils.episode.invalidate(),
});
...
Mutating onSubmit function:
async function onSubmit() {
await form.trigger();
const errors = Object.keys(form.formState.errors);
if (errors.length > 0) {
throw new Error("Form has errors");
}
if (!!id) {
updateEpisode.mutate({
id,
...form.getValues(),
});
} else {
createEpisode.mutate({
...form.getValues(),
});
}
form.reset();
}
async function onSubmit() {
await form.trigger();
const errors = Object.keys(form.formState.errors);
if (errors.length > 0) {
throw new Error("Form has errors");
}
if (!!id) {
updateEpisode.mutate({
id,
...form.getValues(),
});
} else {
createEpisode.mutate({
...form.getValues(),
});
}
form.reset();
}
Specific error is in the screenshot, I assume I'm just doing the client component trpc pattern wrong, but not really sure what a correct call would look like
Solution:
GitHub
Comparing hyhydev:main...varugasu:patch-1 · hyhydev/Modulation
Modulation Podcast Website. Contribute to hyhydev/Modulation development by creating an account on GitHub.
Jump to solution
11 Replies
hyhy
hyhy6mo ago
create: hyhyProtectedProcedure
.input(
z.object({
name: z.string(),
}),
)
.mutation(async ({ ctx, input }) => {
await ctx.db.insert(episodes).values({
name: input.name,
});
}),
update: hyhyProtectedProcedure
.input(
z.object({
id: z.number(),
name: z.string(),
}),
)
.mutation(async ({ ctx, input }) => {
await ctx.db
.update(episodes)
.set({
name: input.name,
})
.where(eq(episodes.id, input.id));
}),
create: hyhyProtectedProcedure
.input(
z.object({
name: z.string(),
}),
)
.mutation(async ({ ctx, input }) => {
await ctx.db.insert(episodes).values({
name: input.name,
});
}),
update: hyhyProtectedProcedure
.input(
z.object({
id: z.number(),
name: z.string(),
}),
)
.mutation(async ({ ctx, input }) => {
await ctx.db
.update(episodes)
.set({
name: input.name,
})
.where(eq(episodes.id, input.id));
}),
simple mutation in case it's helpful at all
Vargas
Vargas6mo ago
Probably an error with your ~/trpc/react.tsx. Do you mind sharing it, please? We need to check TRPCReactProvider And just to make sure, EpisodeForm file contains "use client"?
hyhy
hyhy6mo ago
it does, yeah
"use client";

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { loggerLink, unstable_httpBatchStreamLink } from "@trpc/client";
import { createTRPCReact } from "@trpc/react-query";
import { useState } from "react";

import { type AppRouter } from "~/server/api/root";
import { getUrl, transformer } from "./shared";

export const api = createTRPCReact<AppRouter>();

export function TRPCReactProvider(props: {
children: React.ReactNode;
cookies: string;
}) {
const [queryClient] = useState(() => new QueryClient());

const [trpcClient] = useState(() =>
api.createClient({
transformer,
links: [
loggerLink({
enabled: (op) =>
process.env.NODE_ENV === "development" ||
(op.direction === "down" && op.result instanceof Error),
}),
unstable_httpBatchStreamLink({
url: getUrl(),
headers() {
return {
cookie: props.cookies,
"x-trpc-source": "react",
};
},
}),
],
})
);

return (
<QueryClientProvider client={queryClient}>
<api.Provider client={trpcClient} queryClient={queryClient}>
{props.children}
</api.Provider>
</QueryClientProvider>
);
}
"use client";

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { loggerLink, unstable_httpBatchStreamLink } from "@trpc/client";
import { createTRPCReact } from "@trpc/react-query";
import { useState } from "react";

import { type AppRouter } from "~/server/api/root";
import { getUrl, transformer } from "./shared";

export const api = createTRPCReact<AppRouter>();

export function TRPCReactProvider(props: {
children: React.ReactNode;
cookies: string;
}) {
const [queryClient] = useState(() => new QueryClient());

const [trpcClient] = useState(() =>
api.createClient({
transformer,
links: [
loggerLink({
enabled: (op) =>
process.env.NODE_ENV === "development" ||
(op.direction === "down" && op.result instanceof Error),
}),
unstable_httpBatchStreamLink({
url: getUrl(),
headers() {
return {
cookie: props.cookies,
"x-trpc-source": "react",
};
},
}),
],
})
);

return (
<QueryClientProvider client={queryClient}>
<api.Provider client={trpcClient} queryClient={queryClient}>
{props.children}
</api.Provider>
</QueryClientProvider>
);
}
as far as I can tell it's unmodified from the create-t3-app setup thanks for the help
Vargas
Vargas6mo ago
Indeed. Same as create-t3-app. Can you share the repository? It would be easier to troubleshooting it
hyhy
hyhy6mo ago
Will do, I'll just do a bit of GitHub wrangling to get it public @Vargas i've put it up at https://github.com/hyhydev/Modulation/blob/main/src/app/_components/episode_form/episode_form.tsx, it's a bit messy as was porting over some page router code from a different project here's the page router example from the other project, clicking the + icon in the top header opens this pop up
Vargas
Vargas6mo ago
Just let me setup locally here
hyhy
hyhy6mo ago
no worries, let me know if you need a hand no idea if the episode card loader will work with no data so might need to comment it out in the main app router page
Vargas
Vargas6mo ago
Just found the issue In src/app/layout.tsx, Header must be inside the TRPCReactProvider
Solution
Vargas
Vargas6mo ago
GitHub
Comparing hyhydev:main...varugasu:patch-1 · hyhydev/Modulation
Modulation Podcast Website. Contribute to hyhydev/Modulation development by creating an account on GitHub.
hyhy
hyhy6mo ago
thanks so much, that's a really dumb error of me lol
Vargas
Vargas6mo ago
my pleasure
Want results from more Discord servers?
Add your server
More Posts