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

切换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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 00:54:01