Formik验证在Material UI Autocomplete组件中失效问题排查
解决MUI Autocomplete清除选择时Formik验证不触发的问题
问题原因
当前代码的核心问题是Formik的touched状态未正确更新:
- 初始加载时,Autocomplete有默认值,清除选择后,虽然
parentId被设为null,但formik.touched.parentId仍为false,导致验证错误不显示。 - 提交表单后,Formik会标记所有字段为
touched,此时清除值会触发验证;但再次提交后,touched状态保留,后续清除操作因状态逻辑冲突,无法再次触发验证。
解决方案
需要在Autocomplete的交互流程中,手动维护touched状态并确保验证时机正确:
修改后的完整代码
import "./styles.css"; import { object, string } from "yup"; import { useFormik } from "formik"; import { useState } from "react"; import { Autocomplete, TextField } from "@mui/material"; export default function App() { const parentCategory = [ { id: "983868dc-106d-446b-822c-ff03e62099c6", name: "Electronics", }, { id: "7c7ae7a1-bb3b-4c9d-8da2-91c2bf063875", name: "q", }, { id: "4066deae-7144-40f1-99ab-cc9cf862eb8a", name: "Root", }, { id: "28c9bd93-9f22-4753-8d63-3dcc4839eb07", name: "s", }, { id: "6c4245e1-5cc1-4b07-b718-e47a2e0f33ea", name: "Television", }, ]; const categorySchema = object({ parentId: string().required("Parent category is required.").nullable(), }); const [formData, setFormData] = useState({ name: "", parentId: "4066deae-7144-40f1-99ab-cc9cf862eb8a", }); const initialCategoryValue = parentCategory.find( item => item.id === formData.parentId ); const formik = useFormik({ initialValues: formData, validationSchema: categorySchema, validateOnChange: true, validateOnBlur: true, onSubmit: (values) => { console.log("Submitted values:", values); }, }); return ( <div className="App"> <form onSubmit={formik.handleSubmit}> <Autocomplete id="ddParentCategory" size="small" sx={{ width: 300 }} getOptionLabel={(option) => option.name} getOptionKey={(option) => option.id} options={parentCategory} renderInput={(params) => ( <TextField {...params} label="Parent Category" name="parentId" error={formik.touched.parentId && Boolean(formik.errors.parentId)} helperText={formik.touched.parentId && formik.errors.parentId} onBlur={formik.handleBlur} /> )} isOptionEqualToValue={(option, value) => option.id === value.id} defaultValue={initialCategoryValue} onChange={(e, value, reason) => { formik.setFieldValue("parentId", value?.id || null); formik.setFieldTouched("parentId", true); }} /> <input type="submit" value="Submit" style={{ marginTop: 16 }} /> </form> </div> ); }
关键修改点
- 手动标记
touched状态:在onChange回调中调用formik.setFieldTouched("parentId", true),无论选择或清除操作,都强制标记字段为已触碰,确保验证错误能即时显示。 - 绑定
onBlur事件:给TextField添加onBlur={formik.handleBlur},确保组件失去焦点时也能正确更新touched状态,覆盖更多交互场景。 - 动态匹配初始值:通过
parentCategory.find获取初始选中项,避免硬编码导致的数据源不一致问题。 - 显式配置验证时机:在Formik初始化时明确
validateOnChange和validateOnBlur为true,确保值变化或失去焦点时立即触发验证。
效果验证
- 初始加载后清除选择,立即显示验证错误提示;
- 提交表单后清除选择,验证错误正常显示;
- 多次提交和清除操作,验证逻辑保持稳定一致。
内容的提问来源于stack exchange,提问作者Abhishek Vyas
相关产品推荐
相关产品推荐

