reactjs - React Hooks 表单验证 OnBlur 会触发所有字段,而不仅仅是特定字段

标签 reactjs forms react-hooks onblur

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

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

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

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;

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

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

最佳答案

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

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;

关于reactjs - React Hooks 表单验证 OnBlur 会触发所有字段,而不仅仅是特定字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57874943/

相关文章:

javascript - React - 从列表中间删除会删除最后一个元素

javascript - 使用 Laravel 4 中的表单将 innherHtml 传递的元素上传到数据库

iPhone开发: Use POST to submit a form

表单准备好并经过验证时的 Javascript 条件

javascript - 使用 react 切换钩子(Hook)在两个组件/按钮之间切换{显示一个组件并隐藏另一个}

node.js - npm 错误!代码 UNABLE_TO_GET_ISSUER_CERT_LOCALLY

JavaScript:如何更改数组中对象的属性名称?

html - <pre> 标签和 Paper 之间的空间

javascript - 我们可以在reactjs hooks中使用useState()的对象解构吗?

javascript - 从滚动事件监听器 react setState 钩子(Hook)