[Noob] Not understanding how to create an edit form.

I have 2 major questions/problems, full noob to thank you for any guidance. I am trying to create an edit form for already submitted data, profile related data like username, description etc. During this process, a user picks a URL choice.
defaultValues: {
title: "",
slug: "",
name: "",
serverType: "",
authorId: sessionData?.user?.id,
content: "",
shortDescription: "",
votes: 0,
published: true,
},
defaultValues: {
title: "",
slug: "",
name: "",
serverType: "",
authorId: sessionData?.user?.id,
content: "",
shortDescription: "",
votes: 0,
published: true,
},
The edit form is using the same base, with some removed. This includes slug being removed.
defaultValues: {
title: "",
name: "",
serverType: "",
authorId: sessionData?.user?.id,
content: "",
shortDescription: "",
published: true,
},
defaultValues: {
title: "",
name: "",
serverType: "",
authorId: sessionData?.user?.id,
content: "",
shortDescription: "",
published: true,
},
This is where my questions begin: 1) Because Slug is required, I was getting issues where the default value being loaded in doesn't really work. I am struggling to understand how I load in the data for an edit form to then be re-submitted. Any examples of correct ways to load in data on a page to then be re-submitted? 2) When submiting data during testing, I would get errors related to slug not being included, or already being added. I am using mutate inside trpc so I am again feeling like I am missing some understanding. Thank you!
15 Replies
whatplan
whatplan•2y ago
im a bit confused on what the problem your running into is is the issue on the backend (input/logic of something inside a trpc procedure) or frontend (something with the form behavior/ state)?
Debaucus
DebaucusOP•2y ago
Form state, I wrote something stupid so have decided to try out React Hook Form. I am now not understanding this error in particular -
src/pages/edit2/[pid].tsx (69:14) @ EditServer2

67 | handleSubmit,
68 | formState: { errors },
> 69 | } = useForm({
| ^
70 | resolver: zodResolver(validationSchema),
71 | defaultValues: {
72 | title: userQuery.data[0]?.title,
src/pages/edit2/[pid].tsx (69:14) @ EditServer2

67 | handleSubmit,
68 | formState: { errors },
> 69 | } = useForm({
| ^
70 | resolver: zodResolver(validationSchema),
71 | defaultValues: {
72 | title: userQuery.data[0]?.title,
const EditServer2 = () => {
const sendToLogin = useRouter();
// https://next-auth.js.org/getting-started/client#require-session
const { data: sessionData } = useSession({
required: true,
onUnauthenticated() {
sendToLogin.push("/login");
},
});

const { isReady, query } = useRouter();

const userQuery = trpc.server.getUniqueServer.useQuery(query.pid as string, {
enabled: isReady && typeof query["pid"] === "string",
});

if (!userQuery.data) {
// If the data is not defined, display a custom 404 error message
return (
<>
</>
);
}

if (sessionData?.user?.id != userQuery.data[0]?.authorId) {
return (
<>
</>
);
}

const validationSchema = z.object({
title: z.string().min(5).max(50),
name: z.string().min(1).max(20),
serverType: z.string().min(1).max(20),
authorId: z.string(),
content: z.string().min(300).max(2000),
shortDescription: z.string().min(30).max(200),
published: z.boolean(),
});

const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: zodResolver(validationSchema),
defaultValues: {
title: userQuery.data[0]?.title,
name: userQuery.data[0]?.name,
serverType: userQuery.data[0]?.serverType,
authorId: userQuery.data[0]?.authorId,
content: userQuery.data[0]?.content,
shortDescription: userQuery.data[0]?.shortDescription,
published: userQuery.data[0]?.published,
},
});

return (
<form>
</form>
);
};
const EditServer2 = () => {
const sendToLogin = useRouter();
// https://next-auth.js.org/getting-started/client#require-session
const { data: sessionData } = useSession({
required: true,
onUnauthenticated() {
sendToLogin.push("/login");
},
});

const { isReady, query } = useRouter();

const userQuery = trpc.server.getUniqueServer.useQuery(query.pid as string, {
enabled: isReady && typeof query["pid"] === "string",
});

if (!userQuery.data) {
// If the data is not defined, display a custom 404 error message
return (
<>
</>
);
}

if (sessionData?.user?.id != userQuery.data[0]?.authorId) {
return (
<>
</>
);
}

const validationSchema = z.object({
title: z.string().min(5).max(50),
name: z.string().min(1).max(20),
serverType: z.string().min(1).max(20),
authorId: z.string(),
content: z.string().min(300).max(2000),
shortDescription: z.string().min(30).max(200),
published: z.boolean(),
});

const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: zodResolver(validationSchema),
defaultValues: {
title: userQuery.data[0]?.title,
name: userQuery.data[0]?.name,
serverType: userQuery.data[0]?.serverType,
authorId: userQuery.data[0]?.authorId,
content: userQuery.data[0]?.content,
shortDescription: userQuery.data[0]?.shortDescription,
published: userQuery.data[0]?.published,
},
});

return (
<form>
</form>
);
};
I've removed imports etc due to space, sorry for the big block, but I think its needed for context based on what I've been reading? My understand is the hydration? of the page has to be done on the page at once, and my useForm query is trying to do it more than once.. But I don't understand how I change this in a way that gathers the information correctly. I've got the data, all tested and it outputs when its just placed in logs or divs, but making the edit form update with the data, that I struggle with!
Debaucus
DebaucusOP•2y ago
https://reactjs.org/docs/hooks-rules.html giving this a read now in hopes I understand better 😅
Rules of Hooks – React
A JavaScript library for building user interfaces
Debaucus
DebaucusOP•2y ago
const {
register,
handleSubmit,
setValue,
formState: { errors },
} = useForm({
resolver: zodResolver(validationSchema),
defaultValues: {
title: "",
name: "",
serverType: "",
authorId: "",
content: "",
shortDescription: "",
published: false,
},
});

if (!userQuery.data) {
// If the data is not defined, display a custom 404 error message
return <></>;
}

useEffect(() => {
setValue("title", userQuery.data[0]?.title || "test");
}, []);
const {
register,
handleSubmit,
setValue,
formState: { errors },
} = useForm({
resolver: zodResolver(validationSchema),
defaultValues: {
title: "",
name: "",
serverType: "",
authorId: "",
content: "",
shortDescription: "",
published: false,
},
});

if (!userQuery.data) {
// If the data is not defined, display a custom 404 error message
return <></>;
}

useEffect(() => {
setValue("title", userQuery.data[0]?.title || "test");
}, []);
Same error again, I just feel so lost. Like I am way off the beaten path here.. I want to be able to fill a form with the users previous data, that they can edit and re-submit. Adding data in from a blank slate I got, manipulating the DOM correctly I am not Alright, rubber duck time. Removing the useEffect() allows the page to load without errors, but of course no data is loaded from userQuery. BUT the data is loaded as I see the form, otherwise I would get the if (!userQuery.data) So I need to load in the values that are now loaded. OK!
const userTitle = "pineapple";

const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: zodResolver(validationSchema),
defaultValues: {
title: userTitle,
name: "",
serverType: "",
authorId: "",
content: "",
shortDescription: "",
published: false,
},
});
const userTitle = "pineapple";

const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: zodResolver(validationSchema),
defaultValues: {
title: userTitle,
name: "",
serverType: "",
authorId: "",
content: "",
shortDescription: "",
published: false,
},
});
So even in this example. Its triggering the same error, so its not TRPC specific. I still don't understand the cause, but ANY value causes the runtime error
whatplan
whatplan•2y ago
sorry for late response I was out appreciate you sending all this code, tbh I dont understand it however I can show you some examples of forms ive made and maybe those will help? also definitely use julius's zod wrapper for RHF: https://github.com/juliusmarminge/t3-complete/blob/main/src/utils/zod-form.ts
import { useZodForm } from "~/utils/zod-form";
import { Label } from "~/components/ui/label";
import { Input } from "~/components/ui/input";
import { Button } from "~/components/ui/button";
import { api } from "~/utils/api";

const groupCreateSchema = z.object({
name: z
.string()
.min(1, { message: "Name cannot be blank" })
.max(30, { message: "Name is too long" }),
});

export function GroupCreateForm() {
const methods = useZodForm({
schema: groupCreateSchema,
});
const utils = api.useContext();
const createGroup = api.crud.group.create.useMutation({
onSuccess: async () => {
await utils.crud.group.invalidate();
methods.reset({
name: "",
});
},
});
const onSubmit = methods.handleSubmit(
(data) => {
createGroup.mutate(data);
},
(e) => {
console.log("Whoops... something went wrong!");
console.error(e);
}
);

return (
<form onSubmit={onSubmit}>
<Label htmlFor="name">Name</Label>
<Input {...methods.register("name")} />
<p className="font-medium text-red-500">
{methods.formState.errors?.name?.message}
</p>
<Button type="submit">Create New Group</Button>
</form>
);
}
import { useZodForm } from "~/utils/zod-form";
import { Label } from "~/components/ui/label";
import { Input } from "~/components/ui/input";
import { Button } from "~/components/ui/button";
import { api } from "~/utils/api";

const groupCreateSchema = z.object({
name: z
.string()
.min(1, { message: "Name cannot be blank" })
.max(30, { message: "Name is too long" }),
});

export function GroupCreateForm() {
const methods = useZodForm({
schema: groupCreateSchema,
});
const utils = api.useContext();
const createGroup = api.crud.group.create.useMutation({
onSuccess: async () => {
await utils.crud.group.invalidate();
methods.reset({
name: "",
});
},
});
const onSubmit = methods.handleSubmit(
(data) => {
createGroup.mutate(data);
},
(e) => {
console.log("Whoops... something went wrong!");
console.error(e);
}
);

return (
<form onSubmit={onSubmit}>
<Label htmlFor="name">Name</Label>
<Input {...methods.register("name")} />
<p className="font-medium text-red-500">
{methods.formState.errors?.name?.message}
</p>
<Button type="submit">Create New Group</Button>
</form>
);
}
if this is helpful at all great, if not lmk and I can look further into your specific case
GitHub
t3-complete/zod-form.ts at main · juliusmarminge/t3-complete
Contribute to juliusmarminge/t3-complete development by creating an account on GitHub.
Debaucus
DebaucusOP•2y ago
I've found a solution! A lot of the googling was on how how using useEffect() is the better way to do it, not sure if that advice works with t3 well but it does resolve my issue
whatplan
whatplan•2y ago
nice!
Debaucus
DebaucusOP•2y ago
const {
register,
handleSubmit,
reset,
formState: { errors },
} = useForm({
resolver: zodResolver(validationSchema),
defaultValues: {
title: "Loading..",
name: "Loading..",
serverType: "Loading..",
authorId: "Loading..",
content: "Loading..",
shortDescription: "Loading..",
published: false,
},
});

useEffect(() => {
if (userQueryData) {
reset({
title: userQueryData.title || "",
name: userQueryData.name || "",
serverType: userQueryData.serverType || "",
authorId: userQueryData.authorId || "",
content: userQueryData.content || "",
shortDescription: userQueryData.shortDescription || "",
published: userQueryData.published,
});
}
}, [reset, userQueryData]);
const {
register,
handleSubmit,
reset,
formState: { errors },
} = useForm({
resolver: zodResolver(validationSchema),
defaultValues: {
title: "Loading..",
name: "Loading..",
serverType: "Loading..",
authorId: "Loading..",
content: "Loading..",
shortDescription: "Loading..",
published: false,
},
});

useEffect(() => {
if (userQueryData) {
reset({
title: userQueryData.title || "",
name: userQueryData.name || "",
serverType: userQueryData.serverType || "",
authorId: userQueryData.authorId || "",
content: userQueryData.content || "",
shortDescription: userQueryData.shortDescription || "",
published: userQueryData.published,
});
}
}, [reset, userQueryData]);
So I create the Form, then only once I have the data (the bit I kept getting wrong) does useEffect() trigger and that creates the values in the fields
whatplan
whatplan•2y ago
I see now that looks good to me
Debaucus
DebaucusOP•2y ago
I've noticed it resets when I click off/on the window, but I am guessing that's just because it's dev mode?
whatplan
whatplan•2y ago
where is the userQueryData coming from
Debaucus
DebaucusOP•2y ago
Or does useEffect trigger when window is refocused?
whatplan
whatplan•2y ago
another component? this is almost for sure react querys refetch on window focus
Debaucus
DebaucusOP•2y ago
const { isReady, query } = useRouter();

const userQuery = trpc.server.getUniqueServer.useQuery(query.pid as string, {
enabled: isReady && typeof query["pid"] === "string",
});

const userQueryData = userQuery.data?.[0];
const { isReady, query } = useRouter();

const userQuery = trpc.server.getUniqueServer.useQuery(query.pid as string, {
enabled: isReady && typeof query["pid"] === "string",
});

const userQueryData = userQuery.data?.[0];
So I grab the data from db with trpc, then populate the fields Might have to revisit this in the future, but for now I am just happy to have SOMETHING and move on 🤣 A smarter mind will come back and re-do it all I'm sure, once I've learned a bit more.. Thank you for the help!
whatplan
whatplan•2y ago
sounds good feel free to ping me if you need anything else
Want results from more Discord servers?
Add your server