【问题标题】:How to add input validation in react?如何在反应中添加输入验证?
【发布时间】:2021-04-22 02:06:12
【问题描述】:

我有一个包含firstNamelastName 的简单表单。

    <label htmlFor="firstName">First Name: </label>
    <input
      type="text"
      className="form-control"
      id="firstName"
      name="firstName"
      value={basicDetails.firstName}
      onChange={(event) => handleInputChange(event)}
    />

    <label htmlFor="lastName">Last Name: </label>
    <input
      type="text"
      className="form-control"
      id="lastName"
      name="lastName"
      value={basicDetails.lastName}
      onChange={(event) => handleInputChange(event)}
    />

为此,我正在尝试添加验证。

验证规则是,

  • 两个字段都应该只接受文本
  • 名字是必填项,至少应包含 4 个字符。
  • 如果姓氏字段有值,则至少需要 3 个字符。

我试图实现的目标,

components/utils.js

export function isLettersOnly(string) {
  return /^[a-zA-Z]+$/.test(string);
}

components/basic_details.js

  const handleInputChange = (event) => {
    const { name, value } = event.target;

    if (!isLettersOnly(value)) {
      return;
    }

    setValue((prev) => {
      const basicDetails = { ...prev.basicDetails, [name]: value };
      return { ...prev, basicDetails };
    });
  };

在句柄输入字段上,我正在进行验证以检查输入是否具有值,但我无法理解如何捕捉实际验证错误并显示在相应的输入框下方。

请帮我在各个字段上显示验证消息。

工作示例:

【问题讨论】:

  • 您的所有验证规则都可以单独使用 HTML 完成。检查requiredminlengthpattern 属性。
  • @Evert,现在说清楚,我已经制定了这些验证规则,但实际上我会有更多的验证。我坚持只在 react 的 javascript 部分进行验证,因为需要改变..
  • 您希望何时进行字段验证?每次改变?在现场 onBlur?中间验证是否应该阻止您的上下文/状态更新?
  • 希望被称为TomAmy 的人不想使用您的系统...
  • @DrewReese,是的,验证需要在输入更改时进行,即(输入脏).. 是的,中间验证可以防止上下文/状态更新.. 我们还应该移动 &lt;input&gt;作为一个单独的组件兄弟,因为我觉得我在这里重复了一些事情..

标签: javascript html reactjs forms validation


【解决方案1】:

我建议在form_context的表单数据中添加一个errors属性:

const [formValue, setFormValue] = useState({
  basicDetails: {
    firstName: '',
    lastName: '',
    profileSummary: '',
    errors: {},
  },
  ...
});

将验证添加到basic_details 子表单:

const ErrorText = ({ children }) => (
  <div style={{ color: 'red' }}>{children}</div>
);

const BasicDetails = () => {
  const [value, setValue] = React.useContext(FormContext);
  const { basicDetails } = value;

  const handleInputChange = (event) => {
    const { name, value } = event.target;

    if (!isLettersOnly(value)) {
      setValue((value) => ({
        ...value,
        basicDetails: {
          ...value.basicDetails,
          errors: {
            ...value.basicDetails.errors,
            [name]: 'Can have only letters.',
          },
        },
      }));
      return;
    }

    switch (name) {
      case 'firstName': {
        const error = value.length < 4 ? 'Length must be at least 4.' : null;
        setValue((value) => ({
          ...value,
          basicDetails: {
            ...value.basicDetails,
            errors: {
              ...value.basicDetails.errors,
              [name]: error,
            },
          },
        }));
        break;
      }

      case 'lastName': {
        const error = value.length < 3 ? 'Length must be at least 3.' : null;
        setValue((value) => ({
          ...value,
          basicDetails: {
            ...value.basicDetails,
            errors: {
              ...value.basicDetails.errors,
              [name]: error,
            },
          },
        }));
        break;
      }

      default:
      // ignore
    }

    setValue((prev) => {
      const basicDetails = { ...prev.basicDetails, [name]: value };
      return { ...prev, basicDetails };
    });
  };

  return (
    <>
      <br />
      <br />
      <div className="form-group col-sm-6">
        <label htmlFor="firstName">First Name: </label>
        <input
          type="text"
          className="form-control"
          id="firstName"
          name="firstName"
          value={basicDetails.firstName}
          onChange={(event) => handleInputChange(event)}
        />
      </div>
      <br />
      {basicDetails.errors.firstName && (
        <ErrorText>{basicDetails.errors.firstName}</ErrorText>
      )}
      <br />
      <br />
      <div className="form-group col-sm-4">
        <label htmlFor="lastName">Last Name: </label>
        <input
          type="text"
          className="form-control"
          id="lastName"
          name="lastName"
          value={basicDetails.lastName}
          onChange={(event) => handleInputChange(event)}
        />
      </div>
      <br />
      {basicDetails.errors.lastName && (
        <ErrorText>{basicDetails.errors.lastName}</ErrorText>
      )}
      <br />
    </>
  );
};

最后,检查字段值和错误以在index.js 的下一个按钮上设置disabled 属性。第一个!(value.basicDetails.firstName &amp;&amp; value.basicDetails.lastName) 条件处理初始/空值状态,而第二个条件处理错误值。

{currentPage === 1 && (
  <>
    <BasicDetails />
    <button
      disabled={
        !(
          value.basicDetails.firstName && value.basicDetails.lastName
        ) ||
        Object.values(value.basicDetails.errors).filter(Boolean).length
      }
      onClick={next}
    >
      Next
    </button>
  </>
)}

此模式可以重复执行以下步骤。

【讨论】:

  • 感谢您的快速解决方案,您所做的努力是非常可观的兄弟。您已经清楚地提供了我需要的解决方案。兄弟再次抱歉没有提及其他验证你在 cmets 中问过..我还需要添加模糊验证兄弟..我真的很抱歉没有提到..我一直需要在脏验证和模糊验证上都实现..
  • 另外,如果您有时间,能否请您帮我将输入作为单独的组件? (只是要求兄弟)..
  • @Undefined 由于您正在验证每次更改的输入,我不确定onBlur 验证是否会为您做很多事情,输入将已经在字段之前的最后一个更改事件中得到验证可以注意力不集中。如果可以,我还建议不要重新发明轮子,许多表单字段验证库专门针对您询问的功能而存在。您正在寻找什么样的输入分解?只是一个简单的组件化,让代码更干一点?
  • 谢谢兄弟。我的领导要求我不要包含任何第三方库,并要求我以纯粹的反应方式实现它,这就是我寻求帮助的原因。他还说他不应该看到输入的任何重复代码,所以他希望我将其作为一个单独的组件,并需要将所需的道具传递给该组件..
  • 我不知道我是否让你感到困惑..但是你现在的帮助真的更可观的兄弟..
【解决方案2】:

首先,您必须在控制台中遇到将受控组件转换为不受控组件的错误。对于受控组件,始终首选使用状态来设置输入值。并使用 onChange 处理程序设置状态。我将尝试放入单个组件中,以便您了解想法并应用您的案例

import React, {useState}  from 'react';
import {isLettersOnly} from './components/utils'; // not sure where it is in your folder structure

const MyInputComponent = ({value, ...props}) => {
   const [inputValue, setInputValue] = useState(value || ''); // input value should be empty string or undefined. null will not be accepted.
   const [error, setError] = useState(null);


   const handleChange = event => {
      
    const { name, value } = event.target;

     if (!isLettersOnly(value)) {
         setError('Invalid Input');
     }
      setInputValue(value);
     
   }

   return (
    <>
     <input
      value={inputValue}
      onChange={handleChange}
      {...props}
    />
    {error && (
        <span className={"error"}>{error}</span>
    )}
    </>
   )
}

export default MyInputComponent;

这是一个非常基本的组件。只是为了展示这个概念。然后,您可以将此组件作为输入字段导入,并从父级传递必要的道具,例如名称、类名等。

import React from 'react';
import MyInputComponent from 'components/MyInputComponent';

const MyForm = (props) => {
  return props.data && props.data.map(data=> (
    <MyInputComponent
      name="lastName"
      className="form-control"
      value={data.lastName}
  ));
}

【讨论】:

  • 不,我不能在 useState 中设置这样的值。我从上下文 api 中获取值。如果表单有多个输入,比如这个组件中有 50 个输入,会发生什么情况。我们是否需要包含 50 个 useState 钩子?
  • 根据您在解决方案not sure where it is in your folder structure 中的评论,实用程序位于components/utils.js 下..
  • 我已经更新了评论。 React 的重点是使用 DRY 原则创建可重用的组件。您将创建自己的输入组件,该组件具有已与其他动态道具集成的验证,然后将其用作输入组件。
  • 好的,进行了更多更改以显示如何将其与一组数据一起使用。您可以使用不同的道具传递数据并将此组件乘以任意多次。 value 以外的任何 prop 都将传递给 input,默认情况下它们将被控制和验证,当验证失败时,它们也会在下面填充错误文本。
  • 抱歉,props.value 和 setState 值有冲突。用 inputValue 代替那个。
猜你喜欢
  • 2015-12-23
  • 2017-03-02
  • 2022-09-27
  • 2019-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-12
  • 1970-01-01
相关资源
最近更新 更多