使用反应钩子形式进行条件验证

Posted

技术标签:

【中文标题】使用反应钩子形式进行条件验证【英文标题】:Conditional validation with react hook form 【发布时间】:2021-03-23 05:58:47 【问题描述】:

这是我的表格,也是CodeSanbox。目前我正在使用 react-hook-form 如您所见,表单有 3 个输入。在输入所有必填字段之前,应禁用提交按钮。 两个用例:

    如果未选中“检查”: 只有“id”应该被验证并且提交按钮应该被启用。 “first”和“last”名称不应成为表单数据的一部分 如果选中“检查” 应验证所有字段 只有在选中“检查”时才需要名字和姓氏。所以它没有被检查然后表单应该只验证“ID”字段。如果选中“检查”,则应验证所有字段。

我遇到的问题是,如果我输入 id,表单状态仍然是“无效”。表单需要输入名字和姓氏的值。 我将不胜感激。

【问题讨论】:

【参考方案1】:

我已更新您的 CodeSanBox 代码,并在此处添加完整代码:

import React,  useState, useEffect  from "react";
import ReactDOM from "react-dom";
import  useForm  from "react-hook-form";

import "./index.css";

function App() 
  const 
    register,
    handleSubmit,
    errors,
    formState,
    unregister,
    setValue,
    getValues,
    reset
   = useForm(
    mode: "onBlur",
    reValidateMode: "onBlur",
    shouldUnregister: true
  );
  //console.log(formState.isValid);
  console.log(errors);
  const [disabled, setDisabled] = useState(true);
  const onSubmit = (data) => 
    alert(JSON.stringify(data));
  ;
  useEffect(() => 
    // @ts-ignore

    if (disabled) 
      console.log("unregister");
      reset( ...getValues(), firstName: undefined, lastName: undefined );
      unregister(["firstName", "lastName"]);
     else 
      console.log("register");
      register("firstName",  required: true );
      register("lastName",  required: true );
    
  , [disabled]);

  return (
    <form onSubmit=handleSubmit(onSubmit)>
      <label htmlFor="id">ID</label>
      <input
        name="id"
        placeholder="id"
        ref=register( required: true, maxLength: 50 )
      />
      errors.id && <p>"ID is required"</p>
      <fieldset disabled=disabled>
        <legend>
          <input
            type="checkbox"
            name="name"
            ref=register
            onClick=() => setDisabled(!disabled)
          />
          <span>Check</span>
        </legend>
        <label htmlFor="firstName">First Name</label>
        <input
          name="firstName"
          placeholder="Bill"
          onChange=(e) => 
            console.log(e.target.value);
            setValue("firstName", e.target.value);
          
          ref=register( required: !disabled )
        />
        errors.firstName && <p>"First name is required"</p>
        <label htmlFor="lastName">Last Name</label>
        <input
          name="lastName"
          placeholder="Luo"
          onChange=(e) => setValue("lastName", e.target.value)
          ref=register( required: !disabled )
        />
        errors.lastName && <p>"Last name is required"</p>
      </fieldset>

      <input type="submit" disabled=!formState.isValid />
    </form>
  );


const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

首先我发现您将disabled 状态设置为false,这应该是true 作为初始值,关于这个问题,我在disabled 状态时使用了resetgetValues 函数变化。

EDIT让你更容易识别代码更改,我已经在 CodeSanBox 恢复了所有代码。

【讨论】:

在使用 Joi 或 Yup 等解析器进行验证时如何做同样的事情,有没有办法根据条件动态更改解析器验证? @NabeelHussainShah 如果您使用 Yup,请查看 when 子句【参考方案2】:

整个验证行为 (UX) 肯定会让事情变得更加困难,但是,您应该从库中利用一些东西,例如:

watch validate getValues
import React from "react";
import ReactDOM from "react-dom";
import  useForm  from "react-hook-form";

import "./index.css";

function App() 
  const 
    register,
    handleSubmit,
    errors,
    formState:  isValid, touched ,
    getValues,
    trigger,
    watch
   = useForm(
    mode: "onBlur"
  );
  const onSubmit = (data) => 
    alert(JSON.stringify(data));
  ;
  const validate = (value) => 
    if (getValues("name"))  // read the checkbox value
      return !!value;
    

    return true;
  ;
  const isChecked = watch("name"); // watch if the name is checked

  return (
    <form onSubmit=handleSubmit(onSubmit)>
      <label htmlFor="id">ID</label>
      <input
        name="id"
        placeholder="id"
        ref=register( required: true, maxLength: 50 )
      />
      errors.id && <p>"ID is required"</p>

      <fieldset disabled=!isChecked>
        <legend>
          <input
            type="checkbox"
            name="name"
            ref=register
            onChange=() => trigger() // you want update isValid due to state change, and also those extra two inputs become required
          />
          <span>Check</span>
        </legend>
        <label htmlFor="firstName">First Name</label>
        <input
          name="firstName"
          placeholder="Bill"
          ref=register(
            validate
          )
        />
        // make sure input is touched before fire an error message to the user
        errors.firstName && touched["firstName"] && (
          <p>"First name is required"</p>
        )
        <label htmlFor="lastName">Last Name</label>
        <input
          name="lastName"
          placeholder="Luo"
          ref=register(
            validate
          )
        />
        errors.lastName && touched["lastName"] && (
          <p>"Last name is required"</p>
        )
      </fieldset>

      <input type="submit" disabled=!isValid />
    </form>
  );


const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

CSB: https://codesandbox.io/s/react-hook-form-conditional-fields-forked-n0jig?file=/src/index.js:0-1831

【讨论】:

【参考方案3】:

您只需要替换 true .from ref: required:true..... 而是使用 const 'disabled' ....in 输入名字和姓氏。

从而实现动态变化

【讨论】:

thx @nihal ,我根据您的建议修改了我的代码,但它的行为仍然不理想。另外, mode:'onblur" 是有意使用 formState.isValid ,"formState.isValid" 不适用于 mode:"onSubmit"【参考方案4】:

在你的 ref 上,不要使用硬编码的 bool trueref=register( required: true),而是你的动态 ref=register( required: disabled )

请注意,由于您的 mode: "onBlur" 配置,在 id 字段模糊之前,该按钮将无法使用

【讨论】:

thx @hagai ,我根据您的建议修改了我的代码,但它的行为仍然不理想。另外, mode:'onblur" 是有意使用 formState.isValid ,"formState.isValid" 不适用于 mode:"onSubmit"

以上是关于使用反应钩子形式进行条件验证的主要内容,如果未能解决你的问题,请参考以下文章

反应钩子返回中的简单条件[重复]

反应表单钩子如何验证选择选项

如何使用反应以多步形式进行验证

反应钩子形式 - 类型错误:无法读取 L 处未定义的属性“拆分”(get.ts:6)

[密码并以角度8的反应形式确认密码验证

使用上下文和钩子更新未安装组件的状态 - 反应原生