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

Formik表单提交按钮验证异常:点击后错误信息消失问题

问题解决:Formik提交时错误提示消失而非显示所有字段错误

问题根源

  1. 错误提示依赖touched状态,仅字段被触碰后才显示错误,但点击提交时Formik不会自动将所有字段标记为touched,未触碰字段的错误无法显示。
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 14:17:01