不依赖外部验证库,React.js中实现表单验证

 0
不依赖外部验证库,React.js中实现表单验证

在不依赖外部库的情况下验证React.js表单。不仅会处理验证过程,而且还会提供一些可用于增强表单验证。


有效表单的验证
选择适当的HTML输入类型– 确保使用合适的输入类型(例如email,number)password以利用内置浏览器验证。
利用 HTML 的内置验证属性——利用 HTML 的本机验证属性,例如、、required和来强制执行基本规则,而无需额外的 JavaScript 或库。minlengthmaxlengthpattern
为内置验证器定义自定义工具提示– 自定义工具提示消息,以便在发生验证错误时指导用户。这可以通过使用title输入元素上的属性来实现。
提供特定的验证信息——使用清晰且信息丰富的消息指导用户纠正输入错误。
使用正则表达式进行模式匹配——对于自定义格式,应用正则表达式来验证复杂模式,例如电话号码或邮政编码。
突出显示无效输入——通过视觉上突出显示无效输入的字段,让用户轻松看到需要注意的内容。
当输入有效时隐藏错误消息——确保输入满足验证要求后错误消息消失。
包括服务器端验证——即使在客户端验证之后,也可以通过在服务器上验证表单数据来增强安全性和可靠性。
运行并测试验证


从 GitHub 存储库https://github.com/wpcodevo/form-validation克隆项目。
跳转到validate-reactjs-form目录在Vscode中打开代码。
运行命令pnpm install来安装必要的依赖项。
安装依赖项后,执行命令启动 Vite 开发服务器pnpm dev --port 3000。
最后打开浏览器测试验证逻辑。

React.js中实现表单验证,而不依赖外部验证库。表单将由多个字段组成,每个字段都有其特定的验证错误消息,当输入无效数据时将显示这些消息。

下面是各个字段及其对应的验证错误的概述。

Username– 用户名必须至少包含 3 个字符,并且可以包含字母、数字和下划线。
Email- 请输入有效的电子邮件地址。
Date Of Birth– 请输入有效的日期。
Password– 密码长度必须至少为 8 个字符,并且至少包含一个大写字母、一个小写字母、一个数字和一个特殊字符。
Confirm Password- 密码必须匹配。
仅当用户将焦点移出字段时,如果输入的数据不符合要求的格式,才会显示每个输入的验证错误。以下是说明所有输入字段的验证错误的示例。

使用 React.js 构建的表单上显示的验证错误,不涉及任何外部库
符合要求格式的字段不会显示验证错误,如下图所示。

有些字段无效,而其他字段有效
当输入字段中输入的数据有效时,不会出现任何验证错误,如下图所示。

创建表单并使用 css 进行样式设置
创建表单输入 React 组件
首先创建一个可重复使用的输入组件。为了更加通用将使用React.forwardRef,其引用转发到底层输入元素。这将允许父组件直接访问输入 DOM 元素。

表单输入.tsx

import React, { useState } from 'react';

export interface InputProps
  extends React.InputHTMLAttributes<HTMLInputElement> {
  error?: string;
  label?: string;
}

const FormInput = React.forwardRef<HTMLInputElement, InputProps>(
  ({ type = 'text', name, label, error, className, ...props }, ref) => {
    const [focused, setFocus] = useState(false);
    const handleFocus = () => {
      setFocus(true);
    };
    return (
      <div className={`form-input ${className}`}>
        {label && <label htmlFor={name}>{label}</label>}
        <input
          ref={ref}
          type={type}
          name={name}
          className={`input ${error ? 'input-error' : ''} ${
            focused ? 'input-focused' : ''
          }`}
          onFocus={() => name === 'confirmPassword' && setFocus(true)}
          onBlur={handleFocus}
          {...props}
        />
        {error && <span className='error-message'>{error}</span>}
      </div>
    );
  }
);

FormInput.displayName = 'FormInput';

export { FormInput };

义了一个focus状态并创建了一个名为的函数handleFocus来将此状态设置为true。然后,分配handleFocus给onBlur输入元素的属性。

在React中,onBlur当输入字段失去焦点时会触发此事件,用户点击或退出该字段。onBlur有助于管理焦点状态,用于相应地显示或隐藏错误消息。

在React中创建表单
现在已经定义了输入组件,让我们使用它来呈现表单中的各种输入元素。为了减少冗余并提高代码的可读性在一个对象数组中定义每个输入的属性,然后通过进行映射,使用可重用的组件呈现每个输入。下面是一个示例。

App.tsx

import './App.css';
import { FormInput } from '../components/form-input';

type FormValues = {
  username: string;
  email: string;
  dateOfBirth: string;
  password: string;
  confirmPassword: string;
};

type InputFieldData = {
  id: number;
  type: string;
  name: keyof FormValues;
  placeholder: string;
  label: string;
};

const inputFieldData: InputFieldData[] = [
  {
    id: 1,
    type: 'text',
    name: 'username',
    placeholder: 'Username',
    label: 'Username',
  },
  {
    id: 2,
    type: 'email',
    name: 'email',
    placeholder: 'Email',
    label: 'Email',
  },
  {
    id: 3,
    type: 'date',
    name: 'dateOfBirth',
    placeholder: 'Date Of Birth',
    label: 'Date Of Birth',
  },
  {
    id: 4,
    type: 'password',
    name: 'password',
    placeholder: 'Password',
    label: 'Password',
  },
  {
    id: 5,
    type: 'password',
    name: 'confirmPassword',
    placeholder: 'Confirm Password',
    label: 'Confirm Password',
  },
];

function App() {
  return (
    <div className='wrapper'>
      <form>
        {inputFieldData.map(({ id, name, ...props }) => {
          return <FormInput key={id} name={name} {...props} />;
        })}
        <button type='submit'>Register Account</button>
      </form>
    </div>
  );
}

export default App;

使用CSS来设置表单样式
在验证逻辑之前先设计表单样式并添加背景图像增强外观。

src/App.css

* {
  box-sizing: border-box;
}

:root {
  --primary-color: #007bff;
  --primary-color-hover: #0056b3;
  --border-color: #ccc;
  --error-color: #ff0000;
  --font-family: Arial, sans-serif;
}

body {
  margin: 0;
  font-family: var(--font-family);
  background-image: url('bg.jpg');
  background-size: cover;
  background-position: center;
  color: #333;
}

.wrapper {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  max-width: 500px;
  margin: 0 auto;
}

form {
  display: flex;
  flex-direction: column;
  width: 100%;
  padding: 20px;
  background-color: rgba(255, 255, 255, 0.9);
  border-radius: 8px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}

.form-input {
  display: flex;
  flex-direction: column;
  margin-bottom: 15px;
}

label {
  margin-bottom: 5px;
  font-weight: bold;
}

input {
  padding: 10px;
  border: 1px solid var(--border-color);
  border-radius: 4px;
  font-size: 16px;
  transition: border-color 0.3s ease;
}

input:focus {
  border-color: var(--primary-color);
  outline: none;
}

.error-message {
  color: var(--error-color);
  font-size: 12px;
  margin-top: 10px;
  display: none;
}

input:invalid.input-focused {
  border-color: var(--error-color);
}

input:invalid.input-focused ~ span {
  display: block;
}

button {
  padding: 10px;
  background-color: var(--primary-color);
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 16px;
  transition: background-color 0.3s ease;
}

button:hover {
  background-color: var(--primary-color-hover);
}

button:focus {
  outline: 2px solid var(--primary-color);
}

@media (max-width: 600px) {
  .wrapper {
    max-width: 90%;
  }
}

验证逻辑
完成输入元素的样式设置后,可以继续实现验证逻辑。
HTML5pattern和required属性来处理验证。在输入数据对象中,加字段errorMessage,required每个pattern字段都有适当的值。

import './App.css';
import { FormInput } from '../components/form-input';
import React, { useState } from 'react';

type FormValues = {
  username: string;
  email: string;
  dateOfBirth: string;
  password: string;
  confirmPassword: string;
};

type InputFieldData = {
  id: number;
  type: string;
  name: keyof FormValues;
  placeholder: string;
  label: string;
  error: string;
  pattern?: string;
  required: boolean;
};

const inputFieldData: InputFieldData[] = [
  {
    id: 1,
    type: 'text',
    name: 'username',
    placeholder: 'Username',
    label: 'Username',
    error:
      'Username must be at least 3 characters long and can contain letters, numbers, and underscores.',
    pattern: '^[a-zA-Z0-9_]{3,}$',
    required: true,
  },
  {
    id: 2,
    type: 'email',
    name: 'email',
    placeholder: 'Email',
    label: 'Email',
    error: 'Please enter a valid email address.',
    pattern: '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
    required: true,
  },
  {
    id: 3,
    type: 'date',
    name: 'dateOfBirth',
    placeholder: 'Date Of Birth',
    label: 'Date Of Birth',
    error: 'Please enter a valid date.',
    pattern: '^\\d{4}-\\d{2}-\\d{2}$',
    required: true,
  },
  {
    id: 4,
    type: 'password',
    name: 'password',
    placeholder: 'Password',
    label: 'Password',
    error:
      'Password must be at least 8 characters long, and contain at least one uppercase letter, one lowercase letter, one number, and one special character.',
    pattern: '^(?=.*[A-Z])(?=.*[a-z])(?=.*[\\d\\W])[A-Za-z\\d\\W]{8,}$',
    required: true,
  },
  {
    id: 5,
    type: 'password',
    name: 'confirmPassword',
    placeholder: 'Confirm Password',
    label: 'Confirm Password',
    error: 'Passwords must match.',
    required: true,
  },
];

function App() {
  const [values, setValues] = useState<FormValues>({
    username: '',
    email: '',
    dateOfBirth: '',
    password: '',
    confirmPassword: '',
  });

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setValues((prevValues) => ({
      ...prevValues,
      [name]: value,
    }));
  };

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();

    console.log('Form submitted:', values);
  };

  return (
    <div className='wrapper'>
      <form onSubmit={handleSubmit}>
        {inputFieldData.map(({ id, name, ...props }) => {
          const dynamicPattern =
            name === 'confirmPassword' ? `^${values.password}$` : props.pattern;
          return (
            <FormInput
              key={id}
              name={name}
              value={values[name]}
              onChange={handleChange}
              pattern={dynamicPattern}
              {...props}
            />
          );
        })}
        <button type='submit'>Register Account</button>
      </form>
    </div>
  );
}

export default App;

import './App.css';
import { FormInput } from '../components/form-input';
import React, { useState } from 'react';

type FormValues = {
  username: string;
  email: string;
  dateOfBirth: string;
  password: string;
  confirmPassword: string;
};

type InputFieldData = {
  id: number;
  type: string;
  name: keyof FormValues;
  placeholder: string;
  label: string;
  error: string;
  pattern?: string;
  required: boolean;
};

const inputFieldData: InputFieldData[] = [
  {
    id: 1,
    type: 'text',
    name: 'username',
    placeholder: 'Username',
    label: 'Username',
    error:
      'Username must be at least 3 characters long and can contain letters, numbers, and underscores.',
    pattern: '^[a-zA-Z0-9_]{3,}$',
    required: true,
  },
  {
    id: 2,
    type: 'email',
    name: 'email',
    placeholder: 'Email',
    label: 'Email',
    error: 'Please enter a valid email address.',
    pattern: '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
    required: true,
  },
  {
    id: 3,
    type: 'date',
    name: 'dateOfBirth',
    placeholder: 'Date Of Birth',
    label: 'Date Of Birth',
    error: 'Please enter a valid date.',
    pattern: '^\\d{4}-\\d{2}-\\d{2}$',
    required: true,
  },
  {
    id: 4,
    type: 'password',
    name: 'password',
    placeholder: 'Password',
    label: 'Password',
    error:
      'Password must be at least 8 characters long, and contain at least one uppercase letter, one lowercase letter, one number, and one special character.',
    pattern: '^(?=.*[A-Z])(?=.*[a-z])(?=.*[\\d\\W])[A-Za-z\\d\\W]{8,}$',
    required: true,
  },
  {
    id: 5,
    type: 'password',
    name: 'confirmPassword',
    placeholder: 'Confirm Password',
    label: 'Confirm Password',
    error: 'Passwords must match.',
    required: true,
  },
];

function App() {
  const [values, setValues] = useState<FormValues>({
    username: '',
    email: '',
    dateOfBirth: '',
    password: '',
    confirmPassword: '',
  });

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setValues((prevValues) => ({
      ...prevValues,
      [name]: value,
    }));
  };

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();

    console.log('Form submitted:', values);
  };

  return (
    <div className='wrapper'>
      <form onSubmit={handleSubmit}>
        {inputFieldData.map(({ id, name, ...props }) => {
          const dynamicPattern =
            name === 'confirmPassword' ? `^${values.password}$` : props.pattern;
          return (
            <FormInput
              key={id}
              name={name}
              value={values[name]}
              onChange={handleChange}
              pattern={dynamicPattern}
              {...props}
            />
          );
        })}
        <button type='submit'>Register Account</button>
      </form>
    </div>
  );
}

export default App;

确保密码匹配逻辑正确运行,动态设置输入pattern的属性confirm password匹配密码输入字段的值。

你的反应是什么?

like

dislike

love

funny

angry

sad

wow