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

Formik/Yup/MUI表单文本框带默认值时如何启用提交按钮

问题原因

你直接给TextfieldWrapper传入value属性的写法,只是给MUI输入框DOM设置了显示值,完全没有同步到Formik的内部状态中。Formik存储的batchNo字段值始终是initialValues里的空字符串,Yup校验自然一直提示必填错误,isValid也会一直返回false。
另外你自定义组件里写的...disabled属于未定义变量,运行时会直接抛出引用错误,这个笔误也要先修复。

修复方案

核心原则:所有表单字段的值必须纳入Formik状态管理,禁止直接给输入框硬传value绕开Formik逻辑。

  • 优先在initialValues阶段就把children作为batchNo的初始值,这是最符合Formik设计逻辑的写法。
  • 如果children是异步传入、初始化时拿不到,就在自定义输入框组件挂载时,通过Formik提供的useField暴露的setValue方法把值写入Formik状态。
  • 配置初始校验触发逻辑,保证页面加载时就能算出正确的isValid状态。
  • 调整提交按钮的禁用逻辑,适配默认值场景。

修正后的自定义输入框组件

import React from 'react';
import { TextField } from '@mui/material';
import { useField } from 'formik';

const TextfieldWrapper = ({ name, children, disabled, ...otherProps }) => {
  const [field, meta, helpers] = useField(name);

  // 挂载/children更新时,将传入的默认值同步到Formik状态
  React.useEffect(() => {
    if (children !== undefined && children !== null) {
      // 第二个参数传false表示设置值后不标记为用户修改、不额外触发校验
      helpers.setValue(children, false);
    }
  }, [children, helpers]);

  const configTextfield = {
    ...field,
    disabled: disabled || false,
    ...otherProps,
    fullWidth: true,
    variant: 'outlined',
  };

  if (meta.touched && meta.error) {
    configTextfield.error = true;
    configTextfield.helperText = meta.error;
  }
  return <TextField {...configTextfield} />;
};

修正后的表单代码

const valSchema = Yup.object().shape({
  batchNo: Yup.string().required('Required'),
  item: Yup.string().required('Required'),
  price: Yup.string().required('Required'),
});

const ItemForm = ({ children }) => {
  // 初始化时直接把children设为batchNo默认值
  const initialValues = {
    batchNo: children || '',
    item: '',
    price: ''
  }

  return (
    <Grid container>
      <Grid item xs={12}>
        <Container maxWidth="md">
          <div>
            <Formik
              initialValues={initialValues}
              validationSchema={valSchema}
              // 有默认值时初始就标记字段为已接触,直接触发校验返回正确的isValid状态
              initialTouched={{batchNo: !!children}}
              onSubmit={(values) => {
                localHandleSubmit(values);
              }}
            >
              {({ dirty, isValid, errors }) => (
                <Form>
                  <Grid container spacing={2}>
                    <Grid item xs={12}>
                      {/* 不需要额外写三元分支传value,组件内部自动处理children默认值 */}
                      <TextfieldWrapper name="batchNo" label="Batch No" />
                    </Grid>
                    <Grid item xs={6}>
                      <TextfieldWrapper name="item" label="Item" />
                    </Grid>
                    <Grid item xs={6}>
                      <TextfieldWrapper name="price" label="Price" />
                    </Grid>
                    <Grid item xs={6}>
                      <Typography>{dirty ? 'true' : 'false'}</Typography>
                      <Typography>{isValid ? 'true' : 'false'}</Typography>
                      <Typography>{errors.batchNo}</Typography>
                    </Grid>
                    <Grid item xs={12}>
                      <Button
                        fullWidth
                        variant="contained"
                        // 去掉!dirty判断:如果业务允许直接提交带默认值的表单,不需要等用户修改内容
                        disabled={!isValid}
                        type="submit"
                      >
                        ADD
                      </Button>
                    </Grid>
                  </Grid>
                </Form>
              )}
            </Formik>
          </div>
        </Container>
      </Grid>
    </Grid>
  );
};
关键说明
  • 直接给输入框传value属于DOM层面的显式赋值,不会触发Formik的状态更新和校验逻辑,所有表单值的修改都要通过Formik暴露的field.onChange或者helpers.setValue方法完成。
  • setValue第二个参数传false是为了区分默认值和用户手动输入的值,避免默认值场景下错误触发dirty标记。
  • 如果业务要求必须用户修改过表单才能提交,保留按钮上的!dirty判断即可;如果允许直接提交带默认值的表单,删掉该判断。

内容的提问来源于stack exchange,提问作者0x55b1E06FF

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 08:12:13