如何实现Formik字段输入时实时验证(避免全局触发问题)
实现Formik字段输入时实时验证
Formik默认只在表单提交或字段失焦时触发验证,现在要改成用户输入时就实时验证,但直接删掉touched.字段名的判断会导致所有字段同时显示错误。
解决思路
要搞定这个问题,核心是让Formik只在当前字段输入时验证该字段,同时保留touched状态控制错误显示,避免未交互的空字段提前弹出必填提示。下面是两种可行的实现方式:
方法1:利用Formik默认配置优化错误显示
Formik的useFormik默认就开了validateOnChange: true,会在字段变化时触发验证,但默认是验证整个表单。我们只需要调整错误显示逻辑,让它在用户开始输入当前字段或字段被触摸过时,才显示对应错误。
关键修改点
- 确保每个字段都正确绑定
{...getFieldProps("字段名")},它会自动处理onChange、onBlur事件,触发实时验证并更新touched状态 - 错误显示逻辑改成:
error={Boolean((touched.字段名 || formik.values.字段名) && errors.字段名)},这样用户一输入就触发验证,但没碰过的空字段不会提前显示必填错误
方法2:手动触发单个字段验证(精细控制)
如果需要更灵活的控制,可以在字段的onChange里手动调用formik.validateField("字段名"),只验证当前字段,避免全表单验证。
修改后的完整代码
import { Button, TextField } from "@mui/material"; import { useFormik } from "formik"; import * as Yup from "yup"; import { REGEX } from "../../../Constants/Regex/Regex"; const FormikPractice = () => { const { ONLY_NUMBER_THREE_DIGIT } = REGEX; const CustomerSchema = Yup.object().shape({ parameter: Yup.string() .required("此字段为必填项") .matches(ONLY_NUMBER_THREE_DIGIT, "只能输入三位数字"), message: Yup.string() .required("此字段为必填项") .matches(ONLY_NUMBER_THREE_DIGIT, "只能输入三位数字"), blackList: Yup.string() .required("此字段为必填项") .matches(ONLY_NUMBER_THREE_DIGIT, "只能输入三位数字"), screen: Yup.string() .required("此字段为必填项") .matches(ONLY_NUMBER_THREE_DIGIT, "只能输入三位数字"), }); const formik = useFormik({ initialValues: { parameter: "", message: "", blackList: "", screen: "", }, validationSchema: CustomerSchema, onSubmit: async (values) => console.log(values), // 显式声明开启输入时验证,默认就是true,写出来更直观 validateOnChange: true, }); const { errors, touched, handleSubmit, getFieldProps } = formik; return ( <form style={{ display: "grid", gridTemplateColumns: "repeat(2,1fr)", gap: "1rem", margin: "2rem", }} autoComplete="off" noValidate onSubmit={handleSubmit} > <TextField label="参数" name="parameter" id="parameter" inputProps={{ maxLength: 3 }} {...getFieldProps("parameter")} // 用户输入或字段被触摸时,显示错误 error={Boolean((touched.parameter || formik.values.parameter) && errors.parameter)} helperText={(touched.parameter || formik.values.parameter) && errors.parameter} /> <TextField label="消息" name="message" id="message" inputProps={{ maxLength: 3 }} {...getFieldProps("message")} error={Boolean((touched.message || formik.values.message) && errors.message)} helperText={(touched.message || formik.values.message) && errors.message} /> <TextField label="黑名单" name="blackList" id="blackList" inputProps={{ maxLength: 3 }} {...getFieldProps("blackList")} error={Boolean((touched.blackList || formik.values.blackList) && errors.blackList)} helperText={(touched.blackList || formik.values.blackList) && errors.blackList} /> <TextField label="屏幕" name="screen" id="screen" inputProps={{ maxLength: 3 }} {...getFieldProps("screen")} error={Boolean((touched.screen || formik.values.screen) && errors.screen)} helperText={(touched.screen || formik.values.screen) && errors.screen} /> <Button sx={{ gridColumn: "1/-1" }} type="submit" variant="contained"> 提交 </Button> </form> ); }; export default FormikPractice;
代码说明
- 恢复了原代码中注释掉的
{...getFieldProps("字段名")},确保Formik能正确跟踪字段的输入和触摸状态 - 错误提示逻辑加入了
formik.values.字段名判断,用户开始输入内容时(即使还没失焦)就会触发验证并显示错误,未交互的空字段不会提前弹出必填提示 - 把原代码中的波斯语提示改成了中文,更易读
内容的提问来源于stack exchange,提问作者meysam zeynali
相关产品推荐
相关产品推荐

