reactjs 在Yup验证错误显示在Formik表单中后,使用钩子->意外行为动态更改(i18 n)UI语言

szqfcxe2  于 2023-03-17  发布在  React
关注(0)|答案(3)|浏览(97)

我正在向用户显示一个登录屏幕。这个登录屏幕(或者说整个应用程序,公平地说)可以用不同的语言显示。
除了验证错误之外,语言切换对所有UI组件都正常工作。
目前,如果用户将电子邮件/密码字段留空,或输入无效的电子邮件地址,收到错误消息,然后切换语言,错误消息不会更改为新的语言。
经过大量的猜测和实验,我已经到达了一个点,在点击“de”或“en”按钮两次,然后键入输入后,语言发生了变化。
我一辈子都无法找到useState和useEffect钩子的正确组合,以使验证消息在点击任何一个语言按钮时立即翻译。
以下代码:

import React, { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import { Formik, Field, Form, ErrorMessage } from 'formik'
import * as Yup from 'yup'
import { useTranslation } from 'react-i18next'
import { accountService, alertService } from '@/_services'

function Login({ history, location }) {
  //'common' is defined in i18n.js under init's namespace ('ns:') option
  //if that's not set, use useTranslation('common'), or whatever the name
  //of the json files you use in the language-specific folders is
  const { t, i18n } = useTranslation()

  const [emailRequired, setEmailRequired] = useState(t('login.email_error_required'))
  const [emailInvalid, setEmailInvalid] = useState(t('login.email_error_invalid'))
  const [passwordRequired, setPasswordRequired] = useState(t('login.password_error_required'))

  const [validationSchema, setvalidationSchema] = useState(
    Yup.object().shape({
      email: Yup.string().email(emailInvalid).required(emailRequired),
      password: Yup.string().required(passwordRequired),
    }),
  )

  useEffect(() => {})

  const initialValues = {
    email: '',
    password: '',
  }

  const onSubmit = ({ email, password }, { setSubmitting }) => {
    alertService.clear()
    accountService
      .login(email, password)
      .then(() => {
        const { from } = location.state || { from: { pathname: '/' } }
        history.push(from)
      })
      .catch((error) => {
        setSubmitting(false)
        alertService.error(error)
      })
  }

  return (
    <Formik initialValues={initialValues} validationSchema={validationSchema} onSubmit={onSubmit}>
      {({ errors, touched, isSubmitting }) => (
        <Form>
          <h3 className="card-header">{t('login.page_title')}</h3>
          <div className="card-body">
            <div className="form-group">
              <label>{t('login.email_input_label')}</label>
              <Field name="email" type="text" className={'form-control' + (errors.email && touched.email ? ' is-invalid' : '')} />
              <ErrorMessage name="email" component="div" className="invalid-feedback" />
            </div>
            <div className="form-group">
              <label>{t('login.password_input_label')}</label>
              <Field name="password" type="password" className={'form-control' + (errors.password && touched.password ? ' is-invalid' : '')} />
              <ErrorMessage name="password" component="div" className="invalid-feedback" />
            </div>
            <div className="form-row">
              <div className="form-group col">
                <button type="submit" disabled={isSubmitting} className="btn btn-primary">
                  {isSubmitting && <span className="spinner-border spinner-border-sm mr-1"></span>}
                  {t('login.login_btn_label')}
                </button>
                <Link to="register" className="btn btn-link">
                  {t('login.register_btn_label')}
                </Link>
                <button
                  type="button"
                  onClick={() => {
                    i18n.changeLanguage('de')
                    setEmailRequired(t('login.email_error_required'))
                    setEmailInvalid(t('login.email_error_invalid'))
                    setPasswordRequired(t('login.password_error_required'))
                    setvalidationSchema(
                      Yup.object().shape({
                        email: Yup.string().email(emailInvalid).required(emailRequired),
                        password: Yup.string().required(passwordRequired),
                      }),
                    )
                  }}
                >
                  de
                </button>
                <button
                  type="button"
                  onClick={() => {
                    i18n.changeLanguage('en')
                    setEmailRequired(t('login.email_error_required'))
                    setEmailInvalid(t('login.email_error_invalid'))
                    setPasswordRequired(t('login.password_error_required'))
                    setvalidationSchema(
                      Yup.object().shape({
                        email: Yup.string().email(emailInvalid).required(emailRequired),
                        password: Yup.string().required(passwordRequired),
                      }),
                    )
                  }}
                >
                  en
                </button>
              </div>
              <div className="form-group col text-right">
                <Link to="forgot-password" className="btn btn-link pr-0">
                  {t('login.forgot_password_label')}
                </Link>
              </div>
            </div>
          </div>
        </Form>
      )}
    </Formik>
  )
}

export { Login }

我尝试过的事情及其结果:
(一)
我曾经根本不使用useState,所以validationSchema是这样组成的:

const emailRequired = t('login.email_error_required');
  const emailInvalid = t('login.email_error_invalid');
  const passwordRequired = t('login.password_error_required');

  const validationSchema = Yup.object().shape({
    email: Yup.string().email(emailInvalid).required(emailRequired),
    password: Yup.string().required(passwordRequired),
  })

这导致在键入其中一个输入之前只需要点击一次“de”或“en”按钮就可以将错误消息更改为新的翻译。
(二)
我将语言更改按钮设置为提交类型按钮:

<button
  type="button"
  onClick={() => {
    i18n.changeLanguage('en')
  }}
>

但我觉得这不对。我无法想象这是正确的解决方案。不过,它成功了,因为它只是简单地重新运行了“OnSubmit”函数...
三.)
将“validationSchema”和error变量添加到useState和useEffect钩子中的各种组合,希望它们中的任何一个都能工作,但它根本没有工作。
我想理解的是为什么Formik对象看起来将其内部组件与useEffect带来的更新“隔离”。为什么React没有看到验证错误的语言变化,而是看到了它所做的其他事情?

ipakzgxi

ipakzgxi1#

我还没有收到任何回复,但同时我自己也弄明白了。
原来转换键需要进入模式:

const validationSchema = Yup.object().shape({
    email: Yup.string().email('login.email_error_invalid').required('login.email_error_required'),
    password: Yup.string().required('login.password_error_required'),
  })

然后错误消息需要是自定义组件。因此它们需要从以下内容进行更改:

<ErrorMessage name="password" component="div" className="invalid-feedback" />

对此:

{errors.password && <div className="invalid-feedback">{t(errors.password)}</div>}

对于电子邮件,整个过程如下所示:

<div className="form-group">
  <label>{t('login.password_input_label')}</label>
  <Field name="password" type="password" className={'form-control' + (errors.password && touched.password ? ' is-invalid' : '')} />
  {errors.password && <div className="invalid-feedback">{t(errors.password)}</div>}
</div>

我不知道为什么这会带来不同,但希望它能在未来帮助其他人。

wyyhbhjk

wyyhbhjk2#

谢谢op,它帮我弄清楚了如何让翻译工作。
它对我起作用的方法是:

const LoginSchema = Yup.object().shape({
username: Yup.string().required('loginForm.required'),
password: Yup.string()
    .min(6, 'loginForm.passwordShort')
    .required('loginForm.required'),
});

但是现在提取不起作用,所以你必须手动添加这些密钥到你的.po文件中。

{errors.username && touched.username ? (
                        <IonItem
                            lines="none"
                            className={`${classes.Error} ion-margin-top ion-justify-content-center`}
                        >
                            {t({
                                id: errors.username,
                            })}
                        </IonItem>
                    ) : null}

这种方法适用于Yup + Lingui.js

k2fxgqgv

k2fxgqgv3#

简单,干净,易于打字
//组件外部

const getRegisterFormSchema = (t: TFunction<"translations", undefined, "translations">) => {
    return object({
        firstName: string().required().label(t('PageRegister.labelFirstName')),
    })
}
const PageRegister = () => {
    const {t} = useTranslation()
    const scheme = useMemo(() => getRegisterFormSchema(t), [t]);

    ...

   const methods = useForm<RegisterFormValues>({
        resolver: yupResolver(scheme),
        defaultValues: getInitialValues(),
    });

    const {trigger} = methods; // Extract all you need

     useEffect(() => {
         // translate validation messages immediately, (wrap task to microtask always)
         setTimeout(trigger, 0);
    }, [t]);
}

相关问题