用户端提交个人信息按钮无响应问题排查求助
用户端个人信息提交无反馈问题排查
问题背景
系统支持两种个人信息录入方式:
- 管理员创建
User并录入信息:功能正常 - 用户注册登录后自行填写提交:点击按钮无任何
success/error反馈
流程:邮箱密码注册 → 登录账号 → 填写个人信息 → 通过session获取session.user.email和session.user.id随表单提交
相关代码
FormPage.tsx
const router = useRouter() const form = useForm<AccountSchemaType>({ resolver: zodResolver(AccountSchema), defaultValues: { first_name: '', middle_name: '', last_name: '', sex: '', position: '', position_other: '', classification: '', //DO section_or_unit: '', division_office: '', //Rest years_in_service: '', undergraduate_course: '', date_graduated: '', master_degree: '', doctorate_degree: '', //School school: '', } }) const { mutate, isPending } = useMutation({ mutationFn: CreateAccountAction, onSuccess: (data) => { if(data?.success){ toast({ title: "Success", description: data.success, variant: 'default' }) } router.refresh() if(data?.error){ toast({ title:"Error", description: data.error, variant: "destructive" }) } }, onError: (error: Error) => { console.error("Client error:", error) toast({ title: "Error", description: "Unable to create a new account. Please try again.", variant: "destructive" }) } }) const onSubmit = useCallback((values: AccountSchemaType) => { mutate(values) }, [mutate])
createAccounts.ts
export async function CreateAccountAction(form: AccountSchemaType) { // Get the current session const session = await auth() // Check if user is authenticated if (!session || !session.user || !session.user.id) { return {error: "You are not authorized"} } // Additional check to ensure email exists if (!session.user.email) { return {error: "Email already existing"} } const parsedBody = AccountSchema.safeParse(form) if (!parsedBody.success) { return {error: "Something went wrong"} } const { first_name, middle_name, last_name, sex, position, position_other, classification, section_or_unit, division_office, years_in_service, undergraduate_course, date_graduated, doctorate_degree, master_degree, school } = parsedBody.data try { const newAccount = await db.account.create({ data: { first_name, middle_name, last_name, sex, position, position_other, classification, section_or_unit, undergraduate_course, division_office, years_in_service, date_graduated, master_degree, doctorate_degree, school, // Use non-null assertion since we checked earlier email: session.user.email!, accountId: session.user.id, } }) return {success: "Updated Account", newAccount} } catch (error) { console.error("Error creating account:", error) throw new Error("Failed to create account") } }
zod-schema.ts
export const AccountSchema = z.object({ first_name: z.string().min(1, 'First name is required'), middle_name: z.string().min(1, 'Middle name is required'), last_name: z.string().min(1, 'Last name is required'), sex: z.string().min(2, { message: "Select input field" }), email: z.string().email(), position: z.string().min(1, 'Position is required'), position_other: z.string().optional(), classification: z.string().min(1, 'Classification is required'), //Divsion section_or_unit: z.string().optional(), division_office: z.string().optional(), //Rest years_in_service: z.string().min(1, {message: "Select Years"}), undergraduate_course: z.string().optional(), date_graduated: z.string().optional(), doctorate_degree: z.string().optional(), master_degree: z.string().optional(), //School school: z.string().optional(), }); export type AccountSchemaType = z.infer<typeof AccountSchema>
Schema.Prisma
model User { id String @id @default(cuid()) name String? email String @unique password String? emailVerified DateTime? image String? role String @default("teacher") // "DO / school_admin / teacher" affiliation String? createdAt DateTime @default(now()) updateAt DateTime @updatedAt account Account? } model Account { id String @id @default(cuid()) first_name String middle_name String last_name String sex String email String position String position_other String? classification String years_in_service String section_or_unit String? undergraduate_course String? date_graduated String? doctorate_degree String? master_degree String? school String? division_office String? account User @relation(fields: [accountId], references: [id]) accountId String @unique createdAt DateTime @default(now()) updateAt DateTime @default(now()) @@unique([email]) }
问题分析及修复方案
1. 前端表单校验失败(核心原因)
AccountSchema中定义了email: z.string().email()为必填字段,但前端表单的defaultValues未包含email,且用户提交的表单也不会携带该字段(email从session获取)。React Hook Form 结合 Zod Resolver 会在前端自动校验,校验不通过时会阻止表单提交,导致点击按钮无任何反馈。
修复:
删除AccountSchema中的email字段(或设为可选z.string().email().optional()),因为action已从session获取email,无需前端传递:
export const AccountSchema = z.object({ first_name: z.string().min(1, 'First name is required'), middle_name: z.string().min(1, 'Middle name is required'), last_name: z.string().min(1, 'Last name is required'), sex: z.string().min(2, { message: "Select input field" }), // 移除email字段 position: z.string().min(1, 'Position is required'), position_other: z.string().optional(), classification: z.string().min(1, 'Classification is required'), // 其余字段保持不变 // ... });
2. Action中错误文案逻辑错误
createAccounts.ts中:
if (!session.user.email) { return {error: "Email already existing"} }
逻辑与文案不符,应改为:
if (!session.user.email) { return {error: "Missing email in session"} }
3. 前端Toast显示时机问题
onSuccess中先执行router.refresh()再处理错误Toast,页面刷新会导致Toast来不及显示。调整顺序:
onSuccess: (data) => { if(data?.success){ toast({ title: "Success", description: data.success, variant: 'default' }) } if(data?.error){ toast({ title:"Error", description: data.error, variant: "destructive" }) } // 最后执行页面刷新 router.refresh() },
4. 数据库唯一性冲突处理
Account模型中@@unique([email]),若用户重复提交会触发数据库错误。当前action中catch后throw error,前端onError会触发,但需确保错误信息能准确传递给用户,可修改catch逻辑:
catch (error) { console.error("Error creating account:", error) // 检测是否为唯一性冲突错误 if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2002') { return {error: "Account already exists for this email"} } return {error: "Failed to create account"} // 不要throw,避免进入onError,统一通过返回值处理 }
同时前端onSuccess处理所有返回的success/error,onError仅处理网络等意外错误。
内容的提问来源于stack exchange,提问作者Stykgwar
相关产品推荐
相关产品推荐

