点击提交按钮时Formik无法提交表单,handleSubmit未触发
表单无法提交问题解决(PrimeReact + Formik)
核心问题出在Formik的validate函数返回格式不符合要求,Formik要求validate返回的对象是「字段名: 错误信息字符串」的结构,而你当前返回的是包含布尔值和message的嵌套结构,导致Formik无法正确识别验证状态,直接阻塞了表单提交流程。
具体修正步骤:
1. 重构validate函数
把返回的errors对象改成Formik标准格式:
- 每个字段对应的值直接是错误信息字符串,没有错误时不添加该字段(或设为空字符串)
- 移除多余的
errors.email = false这类布尔值标记
修正后的validate函数:
function handleValidation(values) { const errors = {}; // 邮箱验证 if (values.email && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)) { errors.email = "Invalid email."; } // 确认密码验证 if (values.password !== values.confirmPassword) { errors.confirmPassword = "Password not match."; } // 用户名验证 if (values.username) { if (values.username.length < 3) { errors.username = "Username must have more than 2 characters."; } else if (values.username.length > 21) { errors.username = "Username must have less than 21 characters."; } } // 密码验证 if (values.password) { if (values.password.length < 8) { errors.password = "Password must have more than 8 characters."; } else if (values.password.length > 70) { errors.password = "Password must have less than 70 characters."; // 原代码此处文字错误,已修正 } } return errors; }
2. 调整组件内的错误判断逻辑
因为现在formik.errors.email直接是字符串(或undefined),所以判断是否为错误状态要改成!!formik.errors.email,同时错误信息直接取formik.errors.email:
比如邮箱字段的容器类名:
<div className={"flex flex-col " + (!!formik.errors.email ? cssInvalid : "")}>
InputText的invalid属性:
invalid={!!formik.errors.email}
错误提示small标签:
{formik.errors.email}
同理,把username、password、confirmPassword字段的错误判断都改成这种格式。
3. 其他小问题修正
- 提交按钮的label拼写错误:
Sing In→Sign In - 密码长度上限的错误提示文字修正(原代码误写为"less than 8 characters")
修正后的完整组件代码
import React, { useContext } from "react"; import { useMutation } from "@apollo/client"; import signInMutation from "../apollo/schemas/mutations/signInUser"; import { Dialog } from "primereact/dialog"; import { useFormik } from "formik"; import { InputText } from "primereact/inputtext"; import { FloatLabel } from "primereact/floatlabel"; import { Button } from "primereact/button"; import { Password } from 'primereact/password'; import { ErrorContext } from "../../contexts/ErrorContext"; // eslint-disable-next-line react/prop-types function SignIn({ show, handleClose }) { const cssInvalid = "[&>_*_.p-inputtext]:border-red-500 [&>_*_.p-inputtext]:border-[1px]"; const [newUser, { loading }] = useMutation(signInMutation); const { setErrorMessage } = useContext(ErrorContext); async function handleSubmit(values) { try { const { data } = await newUser({ variables: { username: values.username, email: values.email, password: values.password, }, }); console.log(data.createUser); setErrorMessage(data.createUser); handleClose(); } catch (error) { const errorMsg = error.graphQLErrors?.[0]?.message || "An error occurred"; console.log(errorMsg); setErrorMessage(errorMsg); } } function handleValidation(values) { const errors = {}; // 邮箱验证 if (values.email && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)) { errors.email = "Invalid email."; } // 确认密码验证 if (values.password !== values.confirmPassword) { errors.confirmPassword = "Password not match."; } // 用户名验证 if (values.username) { if (values.username.length < 3) { errors.username = "Username must have more than 2 characters."; } else if (values.username.length > 21) { errors.username = "Username must have less than 21 characters."; } } // 密码验证 if (values.password) { if (values.password.length < 8) { errors.password = "Password must have more than 8 characters."; } else if (values.password.length > 70) { errors.password = "Password must have less than 70 characters."; } } return errors; } const formik = useFormik({ initialValues: { email: "", username: "", password: "", confirmPassword: "", }, onSubmit: (values) => handleSubmit(values), validate: (values) => handleValidation(values), }); return ( <> <Dialog visible={show} modal closable={!loading} onHide={handleClose} content={() => ( <div className="flex flex-col text-center items-center justify-center p-4 gap-4 bg-gradient-to-tr from-slate-700 rounded-xl"> <h1 className="select-none p-3 font-bold">EG</h1> <form className="flex gap-7 flex-col p-4" onSubmit={formik.handleSubmit}> <div className={"flex flex-col " + (!!formik.errors.email ? cssInvalid : "")}> <FloatLabel> <InputText type="email" name="email" invalid={!!formik.errors.email} required={true} onChange={formik.handleChange} onBlur={formik.handleBlur} aria-describedby="email-help" className="p-3" value={formik.values.email} /> <label htmlFor="email">Email</label> </FloatLabel> <small id="email-help" className="text-red-300 select-none"> {formik.errors.email} </small> </div> <div className={"flex flex-col " + (!!formik.errors.username ? cssInvalid : "")}> <FloatLabel> <InputText type="text" name="username" required={true} onChange={formik.handleChange} onBlur={formik.handleBlur} aria-describedby="username-help" className="p-3" value={formik.values.username} /> <label htmlFor="username"> Username </label> </FloatLabel> <small id="username-help" className="text-red-300 select-none"> {formik.errors.username} </small> </div> <div className={"flex flex-col " + (!!formik.errors.password ? cssInvalid : "")}> <FloatLabel> <Password name="password" required={true} onChange={formik.handleChange} onBlur={formik.handleBlur} invalid={!!formik.errors.password} className="[&>_*_.p-password-input]:p-3" value={formik.values.password} toggleMask /> <label htmlFor="password"> Password </label> </FloatLabel> <small id="password-help" className="text-red-300 select-none"> {formik.errors.password} </small> </div> <div className={"flex flex-col " + (!!formik.errors.confirmPassword ? cssInvalid : "")}> <FloatLabel> <Password name="confirmPassword" required={true} onChange={formik.handleChange} onBlur={formik.handleBlur} invalid={!!formik.errors.confirmPassword} className="[&>_*_.p-password-input]:p-3" value={formik.values.confirmPassword} toggleMask /> <label htmlFor="confirmPassword"> Confirm Password </label> </FloatLabel> <small id="confirmPassword-help" className="text-red-300 select-none"> {formik.errors.confirmPassword} </small> </div> <div className="flex align-items-center gap-2"> <Button label="Cancel" disabled={loading || formik.isSubmitting} type="button" onClick={handleClose} text className="p-3 w-full text-primary-50 border-1 border-white-alpha-30 hover:bg-white-alpha-10" /> <Button label="Sign In" type="submit" loading={loading || formik.isSubmitting} text className="p-3 w-full text-primary-50 border-1 border-white-alpha-30 hover:bg-white-alpha-10" /> </div> </form> </div> )} /> </> ); } export default SignIn;
问题根源说明
Formik的validate函数会检查返回的errors对象,只要有非空字符串的字段,就会认为表单验证不通过,阻止onSubmit执行。你之前的返回结构是errors.email = {true/false, message: ''},Formik无法识别这种格式,导致内部验证逻辑异常,直接卡住了提交流程,所以点击提交按钮完全没反应。
内容的提问来源于stack exchange,提问作者Vitor Hugo
相关产品推荐
相关产品推荐

