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

Formik结合Yup实现表单Select字段验证问题求助

Formik中Select字段Yup验证失效的解决方案

问题描述

使用Formik构建的表单包含文本输入框与Select下拉框,通过Yup进行表单验证时,文本字段验证正常,但Select字段无法触发验证。

问题分析

从提供的代码来看,导致Select验证失效的核心原因有三点:

  1. 初始值未正确初始化:mapPropsToValues返回的ulbListForm中,Select对应的字段(如state、district)初始值可能为undefined,Yup.object().required()对undefined的判断逻辑无法触发验证提示。
  2. 自定义FormController未关联Formik验证状态:FormController组件未接收Formik的errors和touched属性,无法在界面上展示验证错误信息。
  3. 值类型与验证规则不匹配:若Select组件实际返回的是字符串(如选项ID),但Yup验证规则写的是Yup.object().required(),会导致验证逻辑完全不匹配。

解决方案

1. 修正初始值设置

在withFormik的mapPropsToValues中,为Select字段设置符合类型要求的默认值(对象类型设为空对象,字符串类型设为空字符串):

mapPropsToValues: (props) => {
  return {
    state: props?.ulbListForm?.state || {},
    district: props?.ulbListForm?.district || {},
    ulbCode: props?.ulbListForm?.ulbCode || "",
    ulbName: props?.ulbListForm?.ulbName || "",
    ulbCategory: props?.ulbListForm?.ulbCategory || {}
  };
},

2. 关联Formik验证状态到FormController

通过Formik的Field组件包裹Select类型的FormController,传递错误和触摸状态,确保验证提示能正常显示:

<Field name="state">
  {({ field, form }) => (
    <FormController
      control="select"
      label="State"
      {...field}
      error={form.touched.state && Boolean(form.errors.state)}
      helperText={form.touched.state && form.errors.state}
      options={props.stateDropdown.data || []}
    />
  )}
</Field>

其他Select字段按照此方式同步修改。

3. 匹配验证规则与实际值类型

根据Select组件实际返回的值类型调整Yup规则:

  • 若返回对象:保持Yup.object().required("Required")
  • 若返回字符串(如选项ID):改为Yup.string().required("Required")

示例调整后的验证规则:

const validationSchema = Yup.object({
  state: Yup.object().required("Required"),
  district: Yup.object().required("Required"),
  ulbCode: Yup.string().required("Required"),
  ulbName: Yup.string().required("Required"),
  ulbCategory: Yup.object().required("Required")
});

修改后的完整表单代码

import { Button, Card, Container, Grid } from "@mui/material";
import { FormController, I18n } from "common/components";
import { Form, Field, withFormik } from "formik";
import React, { useEffect } from "react";
import { connect, useDispatch } from "react-redux";
import * as Yup from "yup";
import { ClearUlb, getAddUlbDetails, getUlbCategorySelect, getUlbDistrictSelect, getUlbStateSelect } from "../actions";
import { useParams } from "react-router-dom";
import { getCategoryDropdownSelect, getDistrictDropdownSelect, getStateDropdownSelect, getUlbListForm } from "../selectors";
import { actions as sliceActions } from "../slice";
import { createStructuredSelector } from "reselect";


const UlbMasterDetailsAdd = (props) => {
  const dispatch = useDispatch();
  const { id = null } = useParams();
  useEffect(() => {
    dispatch(getUlbStateSelect());
    dispatch(getUlbDistrictSelect());
    dispatch(getUlbCategorySelect());
    if (id) {
      // dispatch(getUlbById(id));
    }
    return () => dispatch(ClearUlb());
  }, [id]);
  const containerStyles = {
    display: "flex",
    flexDirection: "column",
    justifyContent: "center",
    alignItem: "center",
    margin: "auto",
    padding: "100px",
    marginLeft: "100px",
    marginRight: "100px"
  };
  const formStyle = {
    padding: "20px"
  };

  return (
    <Card sx={containerStyles}>
      <Container maxWidth="md">
        <Form>
          <Grid container>
            <Grid item xs={6} sx={formStyle}>
              <Field name="state">
                {({ field, form }) => (
                  <FormController
                    control="select"
                    label="State"
                    {...field}
                    error={form.touched.state && Boolean(form.errors.state)}
                    helperText={form.touched.state && form.errors.state}
                    options={props.stateDropdown.data || []}
                  />
                )}
              </Field>
            </Grid>
            <Grid item xs={6} sx={formStyle}>
              <Field name="district">
                {({ field, form }) => (
                  <FormController
                    control="select"
                    label="District"
                    {...field}
                    error={form.touched.district && Boolean(form.errors.district)}
                    helperText={form.touched.district && form.errors.district}
                    options={props.selectDistrict.data || []}
                  />
                )}
              </Field>
            </Grid>
            <Grid item xs={6} sx={formStyle}>
              <FormController
                control="input"
                type="text"
                label="ULB Code"
                name="ulbCode"
              />
            </Grid>
            <Grid item xs={6} sx={formStyle}>
              <FormController
                control="input"
                type="text"
                label="ULB Name"
                name="ulbName"
              />
            </Grid>
            <Grid item xs={6} sx={formStyle}>
              <Field name="ulbCategory">
                {({ field, form }) => (
                  <FormController
                    control="select"
                    label="ULB Category"
                    {...field}
                    error={form.touched.ulbCategory && Boolean(form.errors.ulbCategory)}
                    helperText={form.touched.ulbCategory && form.errors.ulbCategory}
                    options={props.selectCategory.data || []}
                  />
                )}
              </Field>
            </Grid>
          </Grid>
          <Grid sx={{ display: "flex", justifyContent: "center", padding: "20px" }}>
            {id && <Button
              variant="contained"
              type="submit"
            >
              {I18n("update")}
            </Button>}
            {!id && <Button
              variant="contained"
              type="submit"
            >
              {I18n("submit")}
            </Button>}
          </Grid>
        </Form>
      </Container>
    </Card>
  );
};
const mapStateToProps = createStructuredSelector({
  ulbListForm: getUlbListForm,
  stateDropdown: getStateDropdownSelect,
  selectDistrict: getDistrictDropdownSelect,
  selectCategory: getCategoryDropdownSelect
});
const mapDispatchToProps = (dispatch) => ({
  setStoredValue: (data) => dispatch(sliceActions.setUlbListFormData(data)),
  submitValue: (data) => dispatch(getAddUlbDetails(data))
});

const validationSchema = Yup.object({
  state: Yup.object().required("Required"),
  district: Yup.object().required("Required"),
  ulbCode: Yup.string().required("Required"),
  ulbName: Yup.string().required("Required"),
  ulbCategory: Yup.object().required("Required")
});

const addUlbList = withFormik({
  validationSchema,
  enableReinitialize: true,
  mapPropsToValues: (props) => {
    return {
      state: props?.ulbListForm?.state || {},
      district: props?.ulbListForm?.district || {},
      ulbCode: props?.ulbListForm?.ulbCode || "",
      ulbName: props?.ulbListForm?.ulbName || "",
      ulbCategory: props?.ulbListForm?.ulbCategory || {}
    };
  },

  handleSubmit: (values, { props }) => {
    props.submitValue(values);
  },
  displayName: "addUlbList"
})(UlbMasterDetailsAdd);

export default connect(mapStateToProps, mapDispatchToProps)(addUlbList);

补充说明

如果自定义的FormController本身未实现error和helperText的渲染逻辑,需要在FormController内部添加错误提示的展示代码,确保验证失败时用户能看到错误信息。

内容的提问来源于stack exchange,提问作者Arya S Anil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 21:05:23