You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

点击提交按钮时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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 01:54:58