【问题标题】:React Hooks Form Validation OnBlur triggers all fields instead of just specific fieldReact Hooks 表单验证 OnBlur 触发所有字段,而不仅仅是特定字段
【发布时间】:2019-09-10 16:31:56
【问题描述】:

我目前正在使用几个月前在互联网上某个地方找到的这个神奇的钩子,现在找不到我找到的文章了,但基本上我遇到的问题是,当我有一个很大的表格时,我点击下一个字段,如果我使用 onBlur,它会为每个字段运行验证,因此所有需要的字段都会自动变为红色并显示错误(因为它们没有任何内容并且我的错误 css)。

我想等到该字段至少被输入,然后在与该特定字段相关的验证之前离开。

基本上我想知道是否有一种方法可以让我至少等到输入被选择一次。我唯一能想到的就是为表单中的每个输入设置一个单独的钩子,或者以某种方式将一个事件附加到每个字段 - 如果它至少被选中一次,则为真/假,但不知道如何获得实施。

import { useState, useEffect } from "react";

const useFormValidation = (initialState, validate, authenticate) => {
    const [values, setValues] = useState(initialState);
    const [errors, setErrors] = useState({});
    const [isSubmitting, setSubmitting] = useState(false);

    useEffect(() => {
        if (isSubmitting) {
            const noErrors = Object.keys(errors).length === 0;
            if (noErrors) {
                authenticate();
                setSubmitting(false);
            } else {
                setSubmitting(false);
            }
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [errors]);

    const handleChange = (event) => {
        setValues({
            ...values,
            [event.target.name]: event.target.value
        });
    }



    const handleChangeChecked = (event) => {
        setValues({...values, [event.target.name] : event.target.checked });
    }

    //THIS IS THE FUNCTION I AM TALKING ABOUT
    const handleBlur = () => {
        const validationErrors = validate(values);
        setErrors(validationErrors);
    }

    const handleSubmit = (event) => {
        event.preventDefault();
        const validationErrors = validate(values);
        setErrors(validationErrors);
        setSubmitting(true);
    }

    return {
        handleSubmit,
        handleChange,
        handleChangeChecked,
        handleBlur,
        values,
        errors,
        isSubmitting
    };
}

export default useFormValidation;

这是一个示例验证,它也是传递给 useFormValidation 函数的第二个字段。

const validateCareTeam = (values) => {
  let errors = {};

  // First Name Errors
  if (!values.firstName) {
    errors.firstName = "First name is required";
  }

  // Last Name Errors
  if (!values.lastName) {
    errors.lastName = "Last name is required";
  }

// Email Error
if (values.email && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)) {
  errors.email = "Please enter a valid email address";
}

const phoneno = /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/
// Phone Number Errors
if (!values.phone) {
  errors.phoneNumber = "Phone number is required";
} else if (!values.phone.match(phoneno)) {
  errors.phoneNumber = "Please enter a phone number with 10 digits.  1 not necessary"
}

  return errors;
}

export default validateCareTeam;

所以基本上如果在名字后面使用标签,那么所有其他必填字段 - 姓氏和电话号码都会变成红色。

我宁愿在点击提交按钮之前不运行验证,但我被要求立即进行验证。

【问题讨论】:

    标签: reactjs forms react-hooks onblur


    【解决方案1】:

    下面的实现未经测试,但应该知道它应该如何工作,阅读代码中的 cmets

    import {
      useState,
      useEffect
    } from "react";
    
    const useFormValidation = (initialState, validate, authenticate) => {
      const [values, setValues] = useState(initialState);
      const [errors, setErrors] = useState({});
      const [touched, setTouched] = useState([]); // NEW: stores all touched field names
      const [isSubmitting, setSubmitting] = useState(false);
    
      useEffect(() => {
        if (isSubmitting) {
          const noErrors = Object.keys(errors).length === 0;
          if (noErrors) {
            // NEW: probably need to flush touched
            // setTouched([])
            authenticate();
            setSubmitting(false);
          } else {
            setSubmitting(false);
          }
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
      }, [errors]);
    
      const handleChange = (event) => {
        setValues({
          ...values,
          [event.target.name]: event.target.value
        });
        if (!touched.includes(event.target.name)) { // NEW
          setTouched([
            ...touched,
            event.target.name
          ])
        }
      }
    
    
    
      const handleChangeChecked = (event) => {
        setValues({ ...values,
          [event.target.name]: event.target.checked
        });
        if (!touched.includes(event.target.name)) { // NEW: check if we touched this field, then add to touched array
          setTouched([
            ...touched,
            event.target.name
          ])
        }
      }
    
      //THIS IS THE FUNCTION I AM TALKING ABOUT
      const handleBlur = () => {
        const validationErrors = validate(values);
    
        const touchedErrors = Object.keys(validationErrors) // NEW
          .filter(key => touched.includes(key)) // get all touched keys
          .reduce((acc, key) => {
            if (!acc[key]) {
              acc[key] = validationErrors[key]
            }
            return acc
          }, {})
        setErrors(touchedErrors); // NEW: touchedErrors has errors that are touched
      }
    
      const handleSubmit = (event) => {
        event.preventDefault();
        const validationErrors = validate(values);
        // NEW do the same
        const touchedErrors = Object.keys(validationErrors) // NEW
          .filter(key => touched.includes(key)) // get all touched keys
          .reduce((acc, key) => {
            if (!acc[key]) {
              acc[key] = validationErrors[key]
            }
            return acc
          }, {})
        setErrors(touchedErrors); // NEW: touchedErrors has errors that are touched
        setSubmitting(true);
      }
    
      return {
        handleSubmit,
        handleChange,
        handleChangeChecked,
        handleBlur,
        values,
        touched, // NEW: just extend API
        errors,
        isSubmitting
      };
    }
    
    export default useFormValidation;

    【讨论】:

      【解决方案2】:

      Medet Tleukabiluly 使用出色的扩展运算符让我朝着正确的方向前进,并且很接近,如果没有上面的代码,我无法做到这一点,但是因为如果用户在没有输入的情况下切换到下一部分,则不会发生任何变化if 语句的任何输入实际上都需要在touchedErrors 上方的handleBlur 函数中。其他更改包括不需要触摸返回块和提交功能。此外,它不会立即更新 touch,这是 this post 的一个功能 - 基本上它不会运行错误,直到它是 2 个输入。解决此问题的方法是添加一个新的 useEffectHook,每次触摸更改时都会运行。

      这是工作代码,最终应该使用 useCallbacks 来摆脱 eslint 禁用,但现在对我有用。

      import { useState, useEffect } from "react";
      
      const useFormValidation = (initialState, validate, authenticate) => {
          const [values, setValues] = useState(initialState);
          const [errors, setErrors] = useState({});
          const [touched, setTouched] = useState([]);
          const [isSubmitting, setSubmitting] = useState(false);
      
          useEffect(() => {
              if (isSubmitting) {
                  const noErrors = Object.keys(errors).length === 0;
                  if (noErrors) {
                      setTouched([]);
                      authenticate();
                      setSubmitting(false);
                  } else {
                      setSubmitting(false);
                  }
              }
              // eslint-disable-next-line react-hooks/exhaustive-deps
          }, [errors]);
         
          // need to rerun after there is a changed to touched
          // this checks to see if there are any errors that should be highlighted
          useEffect(() => {
                  const validationErrors = validate(values);
                  const touchedErrors = Object.keys(validationErrors)
                  .filter(key => touched.includes(key)) // get all touched keys
                  .reduce((acc, key) => {
                   if (!acc[key]) {
                      acc[key] = validationErrors[key]
                  }
                  return acc
                   }, {})
                  setErrors(touchedErrors);
              // eslint-disable-next-line react-hooks/exhaustive-deps
      
          }, [touched, values]);
      
          const handleChange = (event) => {
              console.log("event changed")
              setValues({
                  ...values,
                  [event.target.name]: event.target.value
              });
          }
      
          const handleChangeChecked = (event) => {
              setValues({...values, [event.target.name] : event.target.checked });
              if (!touched.includes(event.target.name)) {
                  setTouched([
                    ...touched,
                    event.target.name
                  ])
                }
          }
        
          const handleBlur = (event) => {
              if (!touched.includes(event.target.name)) {
                  setTouched([
                    ...touched,
                    event.target.name
                  ])
                }
          }
      
          const handleSubmit = (event) => {
              event.preventDefault();
              const validationErrors = validate(values);
              setErrors(validationErrors);
              setSubmitting(true);
          }
      
          return {
              handleSubmit,
              handleChange,
              handleChangeChecked,
              handleBlur,
              values,
              errors,
              isSubmitting
          };
      }
      
      export default useFormValidation;
      

      【讨论】:

        猜你喜欢
        • 2011-09-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多