Bind values to formAction with useFormState
Answered
Baird's Sandpiper posted this in #help-forum
Baird's SandpiperOP
I have seen examples of using bind on a server action. Is it possible to bind values to the returned formAction from a useFormState( )? Doing so breaks your code
const [formState, formAction] = useFormState(createTemplate, getEmptyFormState());
const { toast } = useToast();
useEffect(() => {
if (formState.status == "SUCCESS") {
toast({
title: "Successfully created system",
description: `sheesh`,
});
redirect("/dashboard/templates");
} else if (formState.status == "ERROR") {
toast({
title: "Something went wrong!",
description: "Please check the information you are submitting.",
variant: "destructive",
});
}
}, [formState.status, formState.timestamp]);
const createWithSessionsAndGoal = createTemplate.bind(null, {
extra: {
goal: options.map((o) => o.value),
sessions: [],
},
});Answered by Ray
try
const [formState, formAction] = useFormState(createTemplate.bind(null, {
extra: {
goal: options.map((o) => o.value),
sessions: [],
},
})), getEmptyFormState();31 Replies
Baird's SandpiperOP
const createWithSessionsAndGoal = formAction.bind(null, {
extra: {
goal: options.map((o) => o.value),
sessions: [],
},
}); doing so breaks the code when trying to use formAction@Baird's Sandpiper I have seen examples of using bind on a server action. Is it possible to bind values to the returned formAction from a useFormState( )? Doing so breaks your code
const [formState, formAction] = useFormState(createTemplate, getEmptyFormState());
const { toast } = useToast();
useEffect(() => {
if (formState.status == "SUCCESS") {
toast({
title: "Successfully created system",
description: `sheesh`,
});
redirect("/dashboard/templates");
} else if (formState.status == "ERROR") {
toast({
title: "Something went wrong!",
description: "Please check the information you are submitting.",
variant: "destructive",
});
}
}, [formState.status, formState.timestamp]);
const createWithSessionsAndGoal = createTemplate.bind(null, {
extra: {
goal: options.map((o) => o.value),
sessions: [],
},
});
try
const [formState, formAction] = useFormState(createTemplate.bind(null, {
extra: {
goal: options.map((o) => o.value),
sessions: [],
},
})), getEmptyFormState();Answer
Baird's SandpiperOP
thank you for the insight! What if I wanted those values to be dynamic? Is that still possible? I need to pass into arrays / objects depending on the form and this is kinda painful. Is it just better to create a seperate submit function that calls the server action and appends the needed data?
not liking this whole bind pattern , but its probably meant for simple use cases not like mine
@Ray try
ts
const [formState, formAction] = useFormState(createTemplate.bind(null, {
extra: {
goal: options.map((o) => o.value),
sessions: [],
},
})), getEmptyFormState();
Baird's SandpiperOP
this actually does not work
Types of parameters 'formData' and 'state' are incompatible.
Type 'FormState' is not assignable to type 'FormData'.@Baird's Sandpiper this actually does not work Types of parameters 'formData' and 'state' are incompatible.
Type 'FormState' is not assignable to type 'FormData'.
if you bind value to your action, the action should look like this
'use server'
export async function createTemplate({extra}, prevState: unknown, formData) {
// ...
}@Ray if you bind value to your action, the action should look like this
ts
'use server'
export async function createTemplate({extra}, prevState: unknown, formData) {
// ...
}
Baird's SandpiperOP
export const createTemplate: FormServerAction = async (_previousState, formData) => {
const { user } = await validateRequest();
if (!user) throw new Error("Unauthorized");
const result = createTemplateSchema.safeParse({
...Object.fromEntries(formData),
});
if (!result.success) {
console.log(fromErrorToFormState(result.error));
return fromErrorToFormState(result.error);
}
try {
await db.insert(template).values({ ...result.data, summary: "", author: user.id });
} catch (error) {
return fromErrorToFormState(error);
}
revalidatePath("/dashboard/templates");
return {
status: "SUCCESS" as const,
error: "",
fieldErrors: {},
timestamp: Date.now(),
};
};export type FormServerAction = (_previousState: unknown, formData: FormData) => Promise<FormState>;the first argument of the server action always returns the previous state even when i try to bind
Baird's SandpiperOP
const [formState, formAction] = useFormState(createTemplate, getEmptyFormState());
const { toast } = useToast();
useEffect(() => {
if (formState.status == "SUCCESS") {
toast({
title: "Successfully created system",
description: `sheesh`,
});
redirect("/dashboard/templates");
} else if (formState.status == "ERROR") {
toast({
title: "Something went wrong!",
description: "Please check the information you are submitting.",
variant: "destructive",
});
}
}, [formState.status, formState.timestamp]);
return (
<div className="w-full max-w-xl space-y-4">
<h2 className="text-2xl font-bold">Create Template</h2>
<div className="rounded border p-6">
<form action={formAction} className="space-y-2"> also remember i am using the formAction form the formState and not the serverAction itselfwhat do you mean?
Baird's SandpiperOP
the first parameter will be previous form state
not any binding
export async function createTemplate( prevState: unknown, {extra}, formData) {
// ...
}try this order
Baird's SandpiperOP
ahh almost
async (bind, _previousState, formData)thanks for the help this was driving me crazy
u smart
Baird's SandpiperOP
ok maybe i overlooked that one lol my bad

Australian Freshwater Crocodile
Hey! So did you make this work?
I have a form with the classic
I have a form with the classic
type="submit" button but i also want another button for saving and exiting, which should get a special argument somehow, but how can i bind this arg to the action returned by useActionState (previously called useFormState)?Australian Freshwater Crocodile
Ended up doing it with both buttons
type="submit" and having the same name but different values and just getting the option from the formData 👍🏽