Formik表单提交按钮验证异常:点击后错误信息消失问题
问题解决:Formik提交时错误提示消失而非显示所有字段错误
问题根源
- 错误提示依赖
touched状态,仅字段被触碰后才显示错误,但点击提交时Formik不会自动将所有字段标记为touched,未触碰字段的错误无法显示。 - Yup验证schema未区分车辆类型,无论电动车还是燃油车,
liters和kWh均被标记为必填,导致总有一个字段无法通过验证,且未渲染的字段无法触发touched状态,干扰错误显示逻辑。
解决方案
步骤1:修正Yup动态验证schema
根据车辆类型调整验证规则,跳过不需要的字段:
import { object, date, string, number } from 'yup'; export const getRefillSchema = (isElectricVehicle) => { const baseSchema = { date: date().required('Date is required'), refillType: string().required('Refill type is required'), kilometers: number().required('Kilometers is required').min(0, 'Kilometers must be at least 0'), price: number().required('Price is required').min(0, 'Price must be at least 0'), }; if (isElectricVehicle) { baseSchema.kWh = number().required('KWh is required').min(0, 'KWh must be at least 0'); baseSchema.liters = number().nullable(); } else { baseSchema.liters = number().required('Liters is required').min(0, 'Liters must be at least 0'); baseSchema.kWh = number().nullable(); } return object(baseSchema); };
步骤2:修改Formik组件配置
动态传入验证schema,并在提交时强制标记所有需要验证的字段为touched:
import React from 'react'; import { ChevronLeftIcon } from '@heroicons/react/20/solid'; import { Formik, Form, Field, ErrorMessage } from 'formik'; import { refillInitialValues } from '../../../utils/constants'; import { getRefillSchema } from '../../../utils/yupSchemas'; import DatePicker from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; export default function HandleFuelConsumption({ refill, electricVehicle, resetHandleVehicle }) { const validationSchema = getRefillSchema(electricVehicle); return ( <div> <Formik initialValues={refill || refillInitialValues} validationSchema={validationSchema} onSubmit={(values, { resetForm }) => { const submitValues = { ...values }; electricVehicle ? delete submitValues.liters : delete submitValues.kWh; console.log(submitValues); // 处理提交逻辑(如API调用) // ... resetForm(); }} > {({ errors, touched, setFieldValue, setTouched, handleSubmit }) => ( <Form onSubmit={(e) => { const fieldsToTouch = electricVehicle ? ['date', 'kilometers', 'refillType', 'price', 'kWh'] : ['date', 'kilometers', 'refillType', 'price', 'liters']; setTouched( fieldsToTouch.reduce((acc, field) => { acc[field] = true; return acc; }, {}) ); handleSubmit(e); }}> <div id="actionsBar" className="p-2 border-b-2"> <div className="flex justify-between"> <div className="flex items-center cursor-pointer text-[#3300FF]" onClick={resetHandleVehicle}> <ChevronLeftIcon className="w-[25px] h-auto" /> <p>Back</p> </div> <button type="submit" className="flex items-center cursor-pointer text-white blue-button hover:opacity-80" > <p>Save</p> </button> </div> </div> <div className="flex flex-wrap p-2 gap-x-6"> <div> <label htmlFor="date" className="block text-sm font-medium text-gray-700"> Date </label> <Field name="date"> {({ field }) => ( <DatePicker id="date" selected={field.value} onChange={(date) => setFieldValue('date', date)} className={`${ errors.date && touched.date ? 'border-red-500' : '' } cursor-default block w-full appearance-none rounded-md border border-gray-300 px-3 py-2 placeholder-gray-400 shadow-sm focus:border-primary focus:outline-none focus:ring-primary-hover sm:text-sm`} showTimeSelect dateFormat="yyyy-MM-dd HH:mm:ss" timeFormat="HH:mm:ss" /> )} </Field> <ErrorMessage name="date" component="p" className="text-red-500 text-xs italic" /> </div> <div> <label htmlFor="kilometers" className="block text-sm font-medium text-gray-700"> Kilometers </label> <div className="mt-1"> <Field id="kilometers" name="kilometers" type="number" min="0" className={`${ errors.kilometers && touched.kilometers ? 'border-red-500' : '' } block w-full appearance-none rounded-md border border-gray-300 px-3 py-2 placeholder-gray-400 shadow-sm focus:border-primary focus:outline-none focus:ring-primary-hover sm:text-sm`} /> </div> <ErrorMessage name="kilometers" component="p" className="text-red-500 text-xs italic" /> </div> <div> <label htmlFor="refillType" className="block text-sm font-medium text-gray-700"> Refill Type </label> <Field as="select" id="refillType" name="refillType" className={`${ errors.refillType && touched.refillType ? 'border-red-500' : '' } min-w-[100px] mt-1 block w-full appearance-none rounded-md border border-gray-300 px-3 py-2 placeholder-gray-400 shadow-sm focus:border-primary focus:outline-none focus:ring-primary-hover sm:text-sm`} > <option value="Full">Full</option> <option value="Partial">Partial</option> </Field> <ErrorMessage name="refillType" component="p" className="text-red-500 text-xs italic" /> </div> </div> <div className="flex flex-wrap p-2 gap-x-6"> <div> <label htmlFor="price" className="block text-sm font-medium text-gray-700"> Price </label> <div className="mt-1"> <Field id="price" name="price" type="number" min="0" className={`${ errors.price && touched.price ? 'border-red-500' : '' } block w-full appearance-none rounded-md border border-gray-300 px-3 py-2 placeholder-gray-400 shadow-sm focus:border-primary focus:outline-none focus:ring-primary-hover sm:text-sm`} /> </div> <ErrorMessage name="price" component="p" className="text-red-500 text-xs italic" /> </div> {electricVehicle ? ( <div> <label htmlFor="kWh" className="block text-sm font-medium text-gray-700"> KWh </label> <div className="mt-1"> <Field id="kWh" name="kWh" type="number" min="0" className={`${ errors.kWh && touched.kWh ? 'border-red-500' : '' } block w-full appearance-none rounded-md border border-gray-300 px-3 py-2 placeholder-gray-400 shadow-sm focus:border-primary focus:outline-none focus:ring-primary-hover sm:text-sm`} /> </div> <ErrorMessage name="kWh" component="p" className="text-red-500 text-xs italic" /> </div> ) : ( <div> <label htmlFor="liters" className="block text-sm font-medium text-gray-700"> Liters </label> <div className="mt-1"> <Field id="liters" name="liters" type="number" min="0" className={`${ errors.liters && touched.liters ? 'border-red-500' : '' } block w-full appearance-none rounded-md border border-gray-300 px-3 py-2 placeholder-gray-400 shadow-sm focus:border-primary focus:outline-none focus:ring-primary-hover sm:text-sm`} /> </div> <ErrorMessage name="liters" component="p" className="text-red-500 text-xs italic" /> </div> )} </div> </Form> )} </Formik> </div> ); }
关键修改说明
- 动态验证schema:根据
electricVehicle参数生成对应字段的验证规则,避免不必要的字段验证报错。 - 提交前强制标记touched:在表单提交前,将所有需要验证的字段标记为已触碰,确保所有错误信息都会显示。
- 过滤提交值:在
onSubmit中过滤掉不需要的字段,避免提交冗余数据。
内容的提问来源于stack exchange,提问作者SnNaCk
相关产品推荐
相关产品推荐

