Next.js 14: Error with useFormState and Server Actions
Answered
maison margiela posted this in #help-forum
Hi everyone! I'm facing an issue while using useFormState in a client component in Next.js 14.
I have a form where, upon submission, I call a server action that validates the input using Zod. If everything is valid, it sends a magic link. However, I'm encountering the following error:
Here’s how I’m using useFormState:
If i comment out that part of code, the error is gone. Also i include my full component and server action
I have a form where, upon submission, I call a server action that validates the input using Zod. If everything is valid, it sends a magic link. However, I'm encountering the following error:
⨯ Internal error: Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Here’s how I’m using useFormState:
const [formState, formAction] = useFormState<FormState, FormData>(
actions.signInMagic,
{
message: "",
error: null,
}
);
If i comment out that part of code, the error is gone. Also i include my full component and server action
Answered by !=tgt
you're using a server func with a form so why dont you use
<form action>
?14 Replies
Black Turnstone
can you share the repo I wasn't able to reproduce the error
and theres a special useTransition hook that you can use for loading state
check your i18n stuff, you might've missed something, send a ss of the page
@Black Turnstone can you share the repo I wasn't able to reproduce the error
Truly i don't know why i can't reproduce that error again. Is it OK for you if i get in touch with you next time i run into the same problem?
@!=tgt you're using a server func with a form so why dont you use `<form action>`?
thank you for pointing out that i can use straight a form action. I refactored my code to use form actions straight to the point and also using useFormStatus. Code looks much better now. I am still new with next.js, so thank you 😂
There is my approach
There is my approach
function SignInMagic() {
const t = useTranslations("Login");
const { pending } = useFormStatus();
const [formState, formAction] = useFormState<FormState, FormData>(
actions.signInMagic,
{
message: "",
error: null,
}
);
return (
<form action={formAction} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="email">{t("email")}</Label>
<Input
id="email"
type="email"
placeholder="your@email.com"
name="email"
className="w-full"
/>
{formState.error && (
<span className="text-sm text-red-500">{formState.error}</span>
)}
{formState.message && (
<span className="text-sm">{formState.message}</span>
)}
</div>
<Button
type="submit"
variant="default"
className="w-full"
disabled={pending}
>
{pending ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
) : (
<Mail className="mr-2 h-4 w-4" />
)}
{t("magicLink")}
</Button>
</form>
);
}
export default SignInMagic;
lgtm
American
Is that
pending
really working? I thought useFormStatus must be used in a child to a formthey're using useFormState
which is diff
American
function SignInMagic() {
const t = useTranslations("Login");
const { pending } = useFormStatus();
@American
function SignInMagic() {
const t = useTranslations("Login");
const { pending } = useFormStatus();
yes, you are right, i then changed my code to fix that and now it is working
"use client";
import * as actions from "@/actions";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Mail, Loader2 } from "lucide-react";
import { useFormStatus } from "react-dom";
import { useTranslations } from "next-intl";
import { useFormState } from "react-dom";
type FormState = {
message: string;
error: string | null;
};
function SubmitButton({ t }: { t: any }) {
const { pending } = useFormStatus();
return (
<Button
type="submit"
variant="default"
className="w-full"
disabled={pending}
>
{pending ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
) : (
<Mail className="mr-2 h-4 w-4" />
)}
{t("magicLink")}
</Button>
);
}
function SignInMagic() {
const t = useTranslations("Login");
const [formState, formAction] = useFormState<FormState, FormData>(
actions.signInMagic,
{
message: "",
error: null,
}
);
return (
<form action={formAction} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="email">{t("email")}</Label>
<Input
id="email"
type="email"
placeholder="your@email.com"
name="email"
className="w-full"
/>
{formState.error && (
<span className="text-sm text-red-500">{formState.error}</span>
)}
{formState.message && (
<span className="text-sm">{formState.message}</span>
)}
</div>
<SubmitButton t={t} />
</form>
);
}
export default SignInMagic;
if you got a solution make sure to mark it as the answer