Replies: 3 comments 2 replies
-
@dopoto Were you able to figure this out? |
Beta Was this translation helpful? Give feedback.
-
The actual shape you can return, as far as I can tell is this type ServerFormState<TFormData, TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>> = {
values: TFormData;
errors: (UnwrapFormAsyncValidateOrFn<TOnServer> | undefined)[];
errorMap: ValidationErrorMap<undefined, undefined, undefined, ... 4 more ..., UnwrapFormAsyncValidateOrFn<...>>;
} If you return a string, or an array of strings they get added to the error map. e.g. doing this: const serverValidate = createServerValidate({
...formOpts,
onServerValidate: ({ value }) => {
if (value.description.length < 5) {
return "description too short";
}
},
}); return this And with an array: const serverValidate = createServerValidate({
...formOpts,
onServerValidate: ({ value }) => {
if (value.description.length < 5) {
return ["description too short", "some other error"];
}
},
}); returns The values are your form shape, so if you want to return a success variable (for example, an ID to navigate to after creation) you could do something like this: export default async function someAction(prev: unknown, formData: FormData) {
try {
const validatedData = await serverValidate(formData);
const newTodo = await db.todos.create({
data: validatedData // Title and description
});
return {
values: {
...validatedData,
todoId: newTodo.id
}
};
} catch (e) {
if (e instanceof ServerValidateError) {
return e.formState;
}
throw e;
}
} Then you can handle it in the client like so: const form = useForm({
...formOpts,
transform: useTransform(
(baseForm) => {
if (state.values?.todoId) { // state.value is undefined on initialization
console.log("todoId", state.values.todoId); // You could do navigation or something here
}
return mergeForm(baseForm, state!);
},
[state]
),
}); However, you lose type safety + this is a really scuffed workaround. Would not recommend. |
Beta Was this translation helpful? Give feedback.
-
Since we're using nextjs and can easily share code between server/client, I'd recommend the following: Put your zod schema somewhere both client & server can import it, probably the same place you put // shared.ts
import z from "zod";
import { formOptions } from "@tanstack/react-form";
export const formOpts = formOptions({
defaultValues: {
title: "",
description: "",
},
});
export const formSchema = z.object({
title: z
.string()
.min(3, "Title must be at least 3 characters long")
.max(100, "Title must be at most 100 characters long"),
description: z
.string()
.max(500, "Description must be at most 500 characters long"),
}); Do client-side validation only, using the zod schema. SInce we're using the schema anyways, this should be fine for showing any errors + we validate on the server against bad actors (I also don't think giving them perfect form errors matters much) // client-component.ts
// Either for the whole form
const form = useForm({
... formOpts,
transform: useTransform((baseForm) => mergeForm(baseForm, state!), [state]),
validators: {
onChange: formSchema,
},
});
// or per field
<form.Field
name="title"
validators={{
onChange: formSchema.shape.title,
}}
>
{(field) => {
return (
... // Your input here
);
}}
</form.Field> Then use a // api/routers/todos.ts
import { formSchema } from "@/lib/shared";
import { createTRPCRouter, publicProcedure } from "@/server/api/trpc";
export const todosRouter = createTRPCRouter({
create: publicProcedure
.input(formSchema)
.mutation(({ input }) => {
... // Logic here
}),
}); and on the client component we simply use // client-component.ts
const createTodo = useMutation(trpc.todos.create.mutationOptions())
const form = useForm({
...formOpts,
transform: useTransform((baseForm) => mergeForm(baseForm, state!), [state]),
onSubmit: async ({value}) => {
// You could try-catch this, and put some errors on the form or show them in a different way
// You can also block the button using field.Subscribe and `isSubmitting`
const todo = await createTodo.mutateAsync(value)
router.push(`todos/${todo.id}`)
},
}); |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I'm looking into how to take in use TanStack Form into a Next.js app using server actions:
I might be missing something obvious, but neither the docs or the code examples show how to actually handle the happy path:
can anybody please point me to some more detailed examples on how to achieve this in a TanStackForm-way?
Beta Was this translation helpful? Give feedback.
All reactions