antd - 表单输入动态验证

antd - Form input dynamic validation

在我的项目中,我在登录界面使用antd Form。如果用户没有提供电子邮件或者如果电子邮件无效,我将使用 antdForm.Item 支持的 rules 属性来处理。

这是我想要实现的目标:

我还想处理另一个错误 'No account associated with email'。用户点击登录按钮后,有一个后端API调用。当后端 returns 在响应中出现此错误时,UI 应在电子邮件输入下显示此错误消息。

为此,我将此错误消息存储在状态变量 'emailError' 中,并在 Form.Item'rules' 属性中使用它,如下所示:

function Login() {
  const [loginForm] = Form.useForm();
  const [emailError, setEmailError] = useState({ enable: false, helpText: "" });
  const emailErrorRef = useRef(emailError);

  const onLoginFormSubmit = async (values) => {
    try {
      let response = await loginRequest(values);
      console.log("Login -> onLoginFormSubmit -> response", response);
      // ...some logic
    } catch (err) {
      /*
        err.response.data = {
          code: 'ERR_LOGIN_NO_ACCOUNT_WITH_EMAIL',
          detail: 'No account associated with this email'
        }
      */
      let errCode = err.response.data.code;
      let errMessage = err.response.data.detail;
      // If the API fails with the below error, then update the state variable.
      if (errCode === "ERR_LOGIN_NO_ACCOUNT_WITH_EMAIL") {
        console.log(
          "Login -> update emailError -> ERR_LOGIN_NO_ACCOUNT_WITH_EMAIL"
        );
        setEmailError({
          enable: true,
          helpText: errMessage
        });
      }
    }
  };

  useEffect(() => {
    console.log("Login -> updated emailError", emailError);
    emailErrorRef.current = emailError;
  }, [emailError]);

  return (
    <div
      style={{
        position: "fixed",
        width: 400,
        height: 200,
        top: "calc(50% - 100px)",
        left: "calc(50% - 200px)"
      }}
    >
      <Form form={loginForm} onFinish={onLoginFormSubmit}>
        <Form.Item
          name="email"
          rules={[
            {
              type: "email",
              message: "Please provide a valid email!"
            },
            { required: true, message: "Please provide your email!" },
            () => ({
              validator() {
                console.log(
                  "Inside email validator! -> emailError",
                  emailError
                );
                if (emailError.enable) {
                  return Promise.reject(emailError.helpText);
                }
                return Promise.resolve();
              }
            })
          ]}
        >
          <Input autoFocus placeholder="Email" prefix={<EmailIcon />} />
        </Form.Item>
        <Form.Item
          name="password"
          rules={[{ required: true, message: "Please provide your password!" }]}
        >
          <Input.Password placeholder="Password" prefix={<PasswordIcon />} />
        </Form.Item>
        <Form.Item>
          <Button type="primary" htmlType="submit">
            Login
          </Button>
        </Form.Item>
      </Form>
    </div>
  );
}

根据我对日志的理解,我认为当我在收到来自异步 API 调用的响应后更新状态变量 'emailError' 时,表单没有被重新呈现.

更新: 我还在 HTML 中记录了状态变量 'emailError'。变化是状态变量反映在 UI 中。所以,我认为重新渲染工作正常。但是,在重新渲染期间,rules 没有被验证..

我曾尝试在 validator() 中使用 'emailErrorRef.current' 而不是 'emailError' 但这没有用。

我在这里错过了什么?请帮忙解决这个问题!

这是一个复制这个的codesandbox:

当您对其中一个字段应用更改以及提交表单时,您的验证会调用,因此当您调用 api 并收到错误时,您的验证将在应用之前调用更改表单或再次提交,解决方案可能是通过在 antd 表单上使用 validateFields 以编程方式调用验证。像这样:

useEffect(() => {
  if(emailError.enable){
    loginForm.validateFields();
  }
}, [emailError]);
...
<Form form={loginForm} 
    onChange={()=> {
      if(emailError.enable)
        setEmailError({enable:false, helpText:''})
    }}
...></Form>

完整示例如下: