添加onChange后MUI TextField导致Formik-Yup验证失效问题求助
解决Formik+Yup+MUI TextField验证失效问题
我明白你现在的困扰——这个问题的核心是你把Formik的表单状态管理和自己手动维护的useState混在一起了,导致Formik无法正确追踪输入框的状态,进而让Yup验证失效。
问题根源
你在父组件里手动维护了firstName的useState,并且给自定义TextField加了value和onChange来更新这个本地状态,但你的自定义TextField已经通过Formik的useField钩子绑定了Formik内部的字段状态(包括值、变更事件等)。这就造成了状态冲突:Formik的内部状态和你的本地firstName不同步,Formik始终认为该字段是空的/未被触碰,所以Yup的必填验证一直触发,即便你实际输入了内容。
而且你强制提交能成功,是因为你提交的是自己本地维护的firstName值,而不是Formik的表单值——但这完全绕开了Formik的验证流程,违背了使用Formik的初衷。
解决方案:放弃手动维护state,完全用Formik管理表单状态
你只需要做以下几个修改:
1. 移除父组件中的firstName相关useState
Formik已经帮你管理所有表单字段的状态,不需要自己额外维护。
2. 修改提交函数,使用Formik提供的表单值
Formik会通过props把values传递给提交函数,直接用这个values来获取字段值即可。
3. 移除TextField上的value和onChange属性
自定义TextField里的useField已经自动绑定了这些属性,手动添加会覆盖Formik的逻辑。
修改后的完整父组件代码
import { useState, useEffect } from 'react' import axios from "axios" import { Container, Grid, Typography} from '@material-ui/core'; import { makeStyles } from '@material-ui/core/styles'; import { Formik, Form } from 'formik'; import * as Yup from 'yup'; import Textfield from './Forms/IndividualComponents/TextField'; import Button from './Forms/IndividualComponents/Button' const useStyles = makeStyles((theme) => ({ formWrapper: { marginTop: theme.spacing(10), marginBottom: theme.spacing(8), }, })); const INITIAL_FORM_STATE = { firstName: '' }; const FORM_VALIDATION = Yup.object().shape({ firstName: Yup.string("enter your first name") .required('Required') }); export default function ImagesUpload() { // 移除手动维护的firstName state const [submitform, setSubmitForm] = useState([]) useEffect(() => { (async() => { const result = await axios.get('/submitform') setSubmitForm(result.data.submitform) })() }, []) // 修改submit函数,接收Formik的values作为参数 const submit = async (values, { resetForm }) => { // 如果后端接收JSON可直接传values,这里保留原FormData逻辑 const data = new FormData() data.append('firstName', values.firstName) const result = await axios.post('/submitForm', data) setSubmitForm([result.data, ...submitform]) // 可选:提交后重置表单 resetForm() } const classes = useStyles(); return ( <Grid item xs={12}> <Container maxWidth="md"> <div className={classes.formWrapper}> <Formik initialValues={{ ...INITIAL_FORM_STATE }} validationSchema={FORM_VALIDATION} onSubmit={submit} // 把submit函数传给Formik的onSubmit > <Form> <Grid container spacing={2}> <Grid item xs={12}> <Typography> Personal details </Typography> </Grid> <Grid item xs={6}> <Textfield name="firstName" label="First Name" type="text" // 移除value和onChange属性 /> </Grid> <button type="submit">Submit</button> </Grid> </Form> </Formik> <main> {submitform.map(post => ( <figure key={post.id}> <figcaption>{post.firstName}</figcaption> </figure> ))} </main> </div> </Container> </Grid> ); }
为什么这样有效?
- Formik的
useField钩子会自动为TextField绑定value和onChange,确保输入框的状态和Formik内部的values、touched同步。 - 当用户输入内容时,Formik会更新内部状态,触发Yup验证,此时验证规则能正确读取到输入框的实际值,不会再误报必填错误。
- 提交时直接使用Formik的
values,既保证了验证通过后才提交,又避免了状态不一致的问题。
你的自定义TextField组件是没问题的,不需要修改——它已经正确使用了Formik的useField来集成MUI的TextField。
内容的提问来源于stack exchange,提问作者pedrosenarego
相关产品推荐
相关产品推荐

