Reactjs React钩子表单验证OnBlur会触发所有字段,而不仅仅是特定字段

Reactjs React钩子表单验证OnBlur会触发所有字段,而不仅仅是特定字段,reactjs,forms,react-hooks,onblur,Reactjs,Forms,React Hooks,Onblur,我目前正在使用几个月前在互联网上某个地方找到的这个神奇的钩子,但我再也找不到我找到它的文章了,但基本上,我遇到的问题是,当我有一个大表单时,如果我使用onBlur,我会切换到下一个字段,它会对每个字段运行验证,因此所有需要的字段都会自动变为红色并显示错误(因为它们中没有任何内容,也没有我的错误css) 我想等待,直到该字段至少已输入,然后离开,然后再进行与该特定字段相关的验证 基本上,我想知道是否有一种方法可以让我等待,直到输入被选中至少一次。我能想到的唯一一件事就是为表单中的每个输入都有一个单

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

我想等待,直到该字段至少已输入,然后离开,然后再进行与该特定字段相关的验证

基本上,我想知道是否有一种方法可以让我等待,直到输入被选中至少一次。我能想到的唯一一件事就是为表单中的每个输入都有一个单独的钩子,或者以某种方式将事件附加到每个字段——如果至少选择了一次,则为true/false,但不知道如何实现

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;
因此,基本上,如果在名字之后使用标签,那么所有其他必填字段-姓氏和电话号码将变为红色


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

下面的实现未经测试,但应该给出它应该如何工作的想法,请阅读代码中的注释

导入{
useState,
使用效果
}从“反应”;
const useFormValidation=(initialState、validate、authenticate)=>{
const[values,setValues]=useState(initialState);
const[errors,setErrors]=useState({});
const[toucted,settoucted]=useState([]);//新建:存储所有被触摸的字段名
const[isSubmitting,setSubmitting]=使用状态(false);
useffect(()=>{
如果(提交){
const noErrors=Object.keys(errors).length==0;
如果(无错误){
//新:可能需要冲洗
//setTouched([])
验证();
设置提交(假);
}否则{
设置提交(假);
}
}
//eslint禁用下一行react HOOK/deps
},[错误];
常量handleChange=(事件)=>{
设定值({
价值观
[event.target.name]:event.target.value
});
如果(!toucted.includes(event.target.name)){//NEW
塞特碰触([
…感动,
event.target.name
])
}
}
常量handleChangeChecked=(事件)=>{
setValues({…值,
[event.target.name]:event.target.checked
});
如果(!toucted.includes(event.target.name)){//NEW:检查是否触及此字段,然后添加到toucted数组
塞特碰触([
…感动,
event.target.name
])
}
}
//这就是我所说的功能
常量车把长度=()=>{
const validationErrors=验证(值);
const touchedErrors=Object.keys(validationErrors)//新建
.filter(key=>toucted.includes(key))//获取所有被触摸的键
.减少((附件,钥匙)=>{
如果(!acc[键]){
acc[key]=验证错误[key]
}
返回acc
}, {})
setErrors(touchedErrors);//新建:touchedErrors有被触摸的错误
}
const handleSubmit=(事件)=>{
event.preventDefault();
const validationErrors=验证(值);
//新员工也会这样做
const touchedErrors=Object.keys(validationErrors)//新建
.filter(key=>toucted.includes(key))//获取所有被触摸的键
.减少((附件,钥匙)=>{
如果(!acc[键]){
acc[key]=验证错误[key]
}
返回acc
}, {})
setErrors(touchedErrors);//新建:touchedErrors有被触摸的错误
设置提交(真);
}
返回{
手推,
handleChange,
手摇开关已检查,
车把,
价值观
触摸,//新建:只需扩展API
错误,
提交
};
}

导出默认useFormValidationMedet Tleukabiluly让我用brilliant spread操作符朝着正确的方向前进,而且距离很近,没有上面的代码,我是不可能做到的,但是,由于如果用户在没有输入任何信息的情况下切换到下一部分,则不会有任何更改,因此if语句实际上需要位于
handleBlur
函数上方的
touchedErrors
中。其他更改包括不需要触摸返回块和提交功能。此外,它不会立即更新touch,这是的一个功能-基本上,它不会运行错误,直到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;