You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React中实现类Angular风格的表单验证?

问题描述

我负责的项目不同模块分别采用Angular和React技术栈。我在Angular中实现了一个表单,借助其内置验证功能检查输入字段是否已填写且合法,例如一个必填的name字段,为空或不合法时会添加has-error类。

以下是我的Angular代码:

<div
  class="form-group"
  ng-class="{ 'has-error' : validating && form.name.$invalid }"
>
  <label for="name" class="col-md-3 control-label">Name</label>
  <div class="col-md-9">
    <input
      type="text"
      id="name"
      name="description"
      ng-model="name"
      class="form-control"
      required
    />
  </div>
</div>

我希望在React中实现类似的表单验证,但不知如何着手,恳请大家提供解决方案:如何在React中实现类Angular风格的表单验证?有哪些相关最佳实践或适用的工具库?


React实现类Angular风格表单验证的方案

一、原生React(受控组件)实现

React没有内置表单验证机制,但可以通过受控组件手动实现和Angular逻辑一致的验证效果,核心是管理表单状态与验证状态:

  1. 定义状态:存储输入值、验证触发标记(如是否正在验证、输入框是否已被触碰)
  2. 编写验证逻辑:判断输入是否符合要求(比如必填校验)
  3. 动态绑定类名:根据验证结果给表单容器添加has-error类

示例代码(函数组件):

import { useState } from 'react';

function NameForm() {
  // 管理输入值、验证状态、输入框触碰状态
  const [name, setName] = useState('');
  const [isValidating, setIsValidating] = useState(false);
  const [isTouched, setIsTouched] = useState(false);

  // 验证规则:判断name是否为空
  const isNameInvalid = () => {
    return !name.trim() && (isValidating || isTouched);
  };

  // 输入变化时更新值,可选触发实时验证
  const handleNameChange = (e) => {
    setName(e.target.value);
    if (isTouched) setIsValidating(true);
  };

  // 输入框失焦时标记为已触碰,并触发验证
  const handleNameBlur = () => {
    setIsTouched(true);
    setIsValidating(true);
  };

  // 表单提交时触发全局验证
  const handleSubmit = (e) => {
    e.preventDefault();
    setIsValidating(true);
    setIsTouched(true);
    if (!isNameInvalid()) {
      // 验证通过,执行提交逻辑
      console.log('提交内容:', name);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <div
        className={`form-group ${isNameInvalid() ? 'has-error' : ''}`}
      >
        <label htmlFor="name" className="col-md-3 control-label">Name</label>
        <div className="col-md-9">
          <input
            type="text"
            id="name"
            name="name"
            className="form-control"
            value={name}
            onChange={handleNameChange}
            onBlur={handleNameBlur}
          />
        </div>
      </div>
      <button type="submit">提交</button>
    </form>
  );
}

export default NameForm;

二、工具库推荐

如果手动实现繁琐,可以用成熟表单库简化开发,效果和Angular内置验证一致:

1. Formik + Yup

Formik负责管理表单状态、提交逻辑,Yup用于定义结构化验证规则,两者配合快速实现复杂验证:

import { useFormik } from 'formik';
import * as Yup from 'yup';

function NameForm() {
  const formik = useFormik({
    initialValues: { name: '' },
    validationSchema: Yup.object({
      name: Yup.string().trim().required('姓名不能为空'),
    }),
    onSubmit: (values) => {
      console.log('提交内容:', values);
    },
  });

  return (
    <form onSubmit={formik.handleSubmit}>
      <div
        className={`form-group ${formik.touched.name && formik.errors.name ? 'has-error' : ''}`}
      >
        <label htmlFor="name" className="col-md-3 control-label">Name</label>
        <div className="col-md-9">
          <input
            type="text"
            id="name"
            name="name"
            className="form-control"
            onChange={formik.handleChange}
            onBlur={formik.handleBlur}
            value={formik.values.name}
          />
          {/* 可选:显示错误提示 */}
          {formik.touched.name && formik.errors.name && (
            <div className="error-text">{formik.errors.name}</div>
          )}
        </div>
      </div>
      <button type="submit">提交</button>
    </form>
  );
}

export default NameForm;

2. React Hook Form

主打性能与简洁,基于React Hooks减少不必要重渲染,支持自定义验证或配合Yup使用:

import { useForm } from 'react-hook-form';
import * as Yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';

function NameForm() {
  const { register, handleSubmit, formState: { errors, touchedFields } } = useForm({
    resolver: yupResolver(Yup.object({
      name: Yup.string().trim().required('姓名不能为空'),
    })),
  });

  const onSubmit = (data) => {
    console.log('提交内容:', data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div
        className={`form-group ${touchedFields.name && errors.name ? 'has-error' : ''}`}
      >
        <label htmlFor="name" className="col-md-3 control-label">Name</label>
        <div className="col-md-9">
          <input
            type="text"
            id="name"
            name="name"
            className="form-control"
            {...register('name')}
          />
          {touchedFields.name && errors.name && (
            <div className="error-text">{errors.name.message}</div>
          )}
        </div>
      </div>
      <button type="submit">提交</button>
    </form>
  );
}

export default NameForm;

三、最佳实践

  • 优先使用受控组件:统一管理表单状态,方便同步验证结果
  • 合理选择验证时机:建议在输入框失焦或表单提交时触发验证,避免输入过程中频繁弹出错误提示
  • 复用验证逻辑:把通用验证规则(如邮箱、手机号格式)抽成独立函数或用Schema定义(如Yup)
  • 清晰的错误反馈:除了添加has-error类,还可显示具体错误文本,帮助用户修正输入
  • 考虑无障碍:错误信息添加aria-describedby属性关联输入框,提升屏幕阅读器兼容性

内容的提问来源于stack exchange,提问作者Mel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 12:45:58