我正在尝试在 Next.js 中进行错误处理。我正在使用传递到表单并在提交表单时调用的服务器操作,如下所示::
表单组件
"use client";
import PasswordInput from "@components/PasswordInput";
import ProvidersLogin from "@components/ProvidersLogin";
import { Button, Divider, Input, Link } from "@nextui-org/react";
import loginCredentials from "@utils/loginCredentials";
import ErrorToast from "@components/ErrorToast";
import { useFormState } from "react-dom";
import { useEffect } from "react";
const initialState = {
message: "",
};
export default function Login() {
const [error, formAction, isPending] = useFormState(
loginCredentials,
initialState
);
useEffect(() => {
console.log("error", error);
}, [error]);
return (
<div className="flex flex-col gap-4 items-center justify-center w-full h-full min-h-screen">
<form action={formAction} className="flex flex-col gap-4 min-w-96">
<h1 className="font-black text-4xl">Login</h1>
<p className="text-default-500 text-lg">
Let{"'"}s make it even better
</p>
<ProvidersLogin />
{/* Commented till error handling is fixed, as currently it does nothing */}
<Divider />
<Input
type="email"
name="email"
label="Email"
labelPlacement="outside"
placeholder="Enter your email"
size="lg"
// biome-ignore lint/a11y/noPositiveTabindex: <explanation>
tabIndex={1}
required
isDisabled={isPending}
// description=""
/>
<div className="relative flex flex-col w-full">
<Link
className="absolute -top-1 tap-highlight-transparent outline-none text-medium text-primary no-underline hover:opacity-80 active:opacity-disabled transition-opacity self-end"
href="/forgotpassword"
>
Forgot Password?
</Link>
<PasswordInput
name="password"
label="Password"
placeholder="Enter your password"
required
// biome-ignore lint/a11y/noPositiveTabindex: <explanation>
tabIndex={2}
isDisabled={isPending}
/>
</div>
<div className="flex flex-row gap-1.5 justify-center items-center w-full -mt-2 -mb-1">
Don{"'"}t have an account?
<Link href="/signup">Sign Up</Link>
</div>
<p>{error?.message}</p>
<div className="flex flex-row gap-4 items-center justify-between">
<Button
as={Link}
href="/"
color="danger"
variant="light"
isDisabled={isPending}
>
Cancel
</Button>
<Button type="submit" color="primary" isLoading={isPending}>
Login
</Button>
</div>
</form>
</div>
);
}
服务器操作
"use server";
import { signIn } from "@auth";
import { redirect, RedirectType } from "next/navigation";
export default async function loginCredentials(
prevState: { message: string },
data: FormData
) {
try {
const email = data.get("email")?.toString();
const password = data.get("password")?.toString();
if (!email || !password) {
throw new Error("Please fill in all fields");
}
await signIn("credentials", {
email,
password,
redirect: false,
// callbackUrl: "/dashboard",
});
} catch (error) {
// @ts-ignore
return { message: error.cause.err.message };
}
redirect("/dashboard", RedirectType.replace);
}
注意:我知道我正在使用
error.cause.err.message
,如next-auth 5,当凭据提供程序授权函数中的CredentialsSignIn错误引发错误时(如果在服务器端调用signIn函数) ,来自 CredentialsSignIn 的消息存储在 error.cause.err.message
中。
问题是当我尝试实现此操作时,服务器操作没有返回响应对象,即
state
仍然存在 undefined
我什至尝试在没有
useFormState
的情况下实现此功能:
表单组件
"use client";
import PasswordInput from "@components/PasswordInput";
import ProvidersLogin from "@components/ProvidersLogin";
import { Button, Divider, Input, Link } from "@nextui-org/react";
import loginCredentials from "@utils/loginCredentials";
import ErrorToast from "@components/ErrorToast";
import { useFormState } from "react-dom";
import { useEffect } from "react";
const initialState = {
message: "",
};
export default function Login() {
const [error, setError] = useState<string>()
const [isPending, setIsPending] = useState(false)
useEffect(() => {
console.log("error", error);
}, [error]);
return (
<div className="flex flex-col gap-4 items-center justify-center w-full h-full min-h-screen">
<form action={async (data) => {
setIsPending(true)
const res = await loginCredentials({message: ""}, data)
if (res?.message) {
setError(res.message)
}
}} className="flex flex-col gap-4 min-w-96">
<h1 className="font-black text-4xl">Login</h1>
<p className="text-default-500 text-lg">
Let{"'"}s make it even better
</p>
<ProvidersLogin />
{/* Commented till error handling is fixed, as currently it does nothing */}
<Divider />
<Input
type="email"
name="email"
label="Email"
labelPlacement="outside"
placeholder="Enter your email"
size="lg"
// biome-ignore lint/a11y/noPositiveTabindex: <explanation>
tabIndex={1}
required
isDisabled={isPending}
// description=""
/>
<div className="relative flex flex-col w-full">
<Link
className="absolute -top-1 tap-highlight-transparent outline-none text-medium text-primary no-underline hover:opacity-80 active:opacity-disabled transition-opacity self-end"
href="/forgotpassword"
>
Forgot Password?
</Link>
<PasswordInput
name="password"
label="Password"
placeholder="Enter your password"
required
// biome-ignore lint/a11y/noPositiveTabindex: <explanation>
tabIndex={2}
isDisabled={isPending}
/>
</div>
<div className="flex flex-row gap-1.5 justify-center items-center w-full -mt-2 -mb-1">
Don{"'"}t have an account?
<Link href="/signup">Sign Up</Link>
</div>
<p>{error}</p>
<div className="flex flex-row gap-4 items-center justify-between">
<Button
as={Link}
href="/"
color="danger"
variant="light"
isDisabled={isPending}
>
Cancel
</Button>
<Button type="submit" color="primary" isLoading={isPending}>
Login
</Button>
</div>
</form>
</div>
);
}
但是服务器操作返回
undefined
,我还检查了服务器操作实际上被调用并且它被调用了。
请有人提供解决方案
注意:我知道
useFormState
将被useActionState
取代,我尝试了一下,它仍然返回undefined
我终于明白了。
问题是,一旦我停止将标题传递给
middleware
,我就试图修改 NextResponse.next()
中的标题,问题已解决。