切换Context语言时如何立即更新Formik+Yup的表单校验错误值
问题原因
Formik 仅会在表单值变动、输入框失焦、提交操作触发时自动执行校验。你切换多语言时,虽然校验规则contactSchema已经使用新语言重新生成,但 Formik 没有主动触发重新校验,内存中存储的errors对象还是旧语言的错误信息,只有操作输入框触发单字段校验后才会更新。
修复方案
第一步:用useMemo包裹校验规则
先给contactSchema加上useMemo,依赖设置为语言变量lang,确保语言切换后始终使用最新的多语言文案生成校验规则,同时避免不必要的重复创建:
import { useMemo, useEffect } from 'react'; import { useFormik } from 'formik'; // 建议改用useFormik Hook更方便操作 function ContactForm() { const [lang] = useContext(LangContext); const initialValues = { email: '', name: '', message: '' }; // 用useMemo包裹校验规则 const contactSchema = useMemo(() => Yup.object().shape({ email: Yup.string() .email(lang.contact.writeMe.emailIncorrect) .required(lang.contact.writeMe.emailError), name: Yup.string() .required(lang.contact.writeMe.nameError), message: Yup.string() .required(lang.contact.writeMe.messageError), }), [lang]);
第二步:改用useFormik Hook + 监听语言变化触发重新校验
把原来的<Formik>组件写法换成useFormik Hook,可以直接拿到全局校验方法validateForm,监听lang变化时主动调用重新校验,既会更新错误文案,也不会清空用户已输入的内容和已触发的touched状态:
// 初始化Formik实例 const formik = useFormik({ initialValues: initialValues, validationSchema: contactSchema, onSubmit: (values, { setSubmitting, resetForm }) => { const timeOut = setTimeout(() => { setSubmitting(false); clearTimeout(timeOut); }, 1500); sendEmail(values, resetForm); } }); // 监听语言变化,主动触发全表单校验 useEffect(() => { formik.validateForm(); }, [lang, formik]);
第三步:修改表单取值逻辑
原来表单里的values/errors/touched/handleChange等方法都从formik实例上取即可,其他渲染逻辑完全不用改:
return ( <section id="contact" className="bg-dark "> <div className="container"> <div className="wrapper"> <div className="row no-gutters"> <div className="col-md-6 d-flex align-items-stretch"> <div className="contact-wrap w-100 p-md-5 p-4 py-5"> <form method="POST" id="contactForm" name="contactForm" className="contactForm" noValidate="novalidate" onSubmit={formik.handleSubmit} > <h3 className="mb-4">{lang.contact.writeMe.title}</h3> {/* 其他文案渲染逻辑不变 */} <div className="row"> <div className="col-md-12 mb-1"> <div className="form-group"> <input type="email" className={`form-control ${formik.touched.email ? (formik.errors.email ? 'incorrect' : 'correct') : ''}`} name="email" id="email" placeholder={lang.contact.writeMe.emailInput} onChange={formik.handleChange} onBlur={formik.handleBlur} value={formik.values.email} /> <p className="text-danger">{formik.errors.email && formik.touched.email && formik.errors.email}</p> </div> </div> {/* 剩余两个字段同上面的修改逻辑,所有属性从formik实例取即可 */} </div> </form> </div> </div> </div> </div> </div> </section> ); }
如果你不想改用useFormik Hook
可以把<Formik>内部的表单内容抽成单独的子组件,在子组件中通过useFormikContext()拿到validateForm方法,同样加useEffect监听lang触发校验即可。
内容的提问来源于stack exchange,提问作者ManuLisek
相关产品推荐
相关产品推荐

