寻找最佳 React 表单库?它可能就在这个列表中

2025-06-10

寻找最佳 React 表单库?它可能就在这个列表中

表单用于收集用户数据以供处理。如今,许多网站都有一个或多个表单。如果您使用过 React,您就会知道它提供了一种使用受控组件来处理表单的方法。但是,如果您构建了大量表单,并且可能还需要验证并跟踪访问过的字段或表单状态,那么大量重复的代码可能会变得繁琐乏味。因此,您可能需要一个表单库,它可以帮助您更轻松地构建具有验证和状态管理功能的各种复杂程度的表单。

在这篇文章中,我将列出一些值得考虑的 React 表单库。我将包含一些用于收集数据的表单代码片段,您将了解每个表单的语法以及它们在使用上的差异。

福米克

Formik是 React 中构建表单的热门库之一(GitHub 上有 26.2k 个 star)。Formik 可以帮助您管理表单状态、处理提交、格式化和验证表单值。它体积也很小,压缩和最小化后只有13.1 kB,支持 TypeScript,并且兼容 React Native。

以下是使用 Formik 编写表单来收集用户数据的方法:

import { Formik, Form, Field, ErrorMessage } from "formik";

const DataForm = () => (
  <>
    <h1>Your Data</h1>
    <Formik
      initialValues={{ name: "", email: "", acceptedTerms: false }}
      validate={(values) => {
        const errors = {};
        if (!values.name) {
          errors.name = "Required";
        }

        if (!values.acceptedTerms) {
          errors.acceptedTerms =
            "You must accept the terms and conditions before you proceed.";
        }

        if (!values.email) {
          errors.email = "Required";
        } else if (
          !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
        ) {
          errors.email = "Invalid email address";
        }
        return errors;
      }}
      onSubmit={(values, { setSubmitting }) => {
        // post data to server
        alert(JSON.stringify(values, null, 2));
        setSubmitting(false);
      }}
    >
      {({ isSubmitting, dirty, handleReset }) => (
        <Form>
          <div>
            <label>
              Name
              <Field type="text" name="name" />
            </label>
            <ErrorMessage name="name" component="span" />
          </div>
          <div>
            <label htmlFor="email">Email</label>
            <Field type="email" name="email" />
            <ErrorMessage name="email" component="span" />
          </div>
          <div>
            <label>Accept terms</label>
            <Field type="checkbox" name="acceptedTerms" />
            <ErrorMessage name="acceptedTerms" component="span" />
          </div>
          <button
            type="button"
            onClick={handleReset}
            disabled={!dirty || isSubmitting}
          >
            Reset
          </button>
          <button type="submit" disabled={isSubmitting}>
            Submit
          </button>
        </Form>
      )}
    </Formik>
  </>
);

export default DataForm;
Enter fullscreen mode Exit fullscreen mode

Formik 自带一些组件,可以更轻松地管理表单状态,并通过 props 公开表单数据。您可以使用组件包装表单<Formik />,并向其传递 props。在本例中,我传入了 prop for initialValues,它是一个对象,其键值与它应该绑定到的字段的name键值匹配id,以及渲染时字段的值。

proponSubmit是一个函数,当表单提交且表单值有效时会被调用。如果表单无效,则会使用<ErrorMessage />组件为每个字段显示错误消息。相比检查错误状态和字段是否被访问,我更喜欢使用<ErrorMessage />。也就是说,<ErrorMessage name="email" component="span" />用 代替{errors.email && touched.email && <span>errors.email</span>}

您可以通过指定或 的validateprops来使用字段级验证或表单级验证。对于字段级验证,您可以指定一个返回错误消息的同步或异步函数;对于表单级验证,您可以指定一个键与相应字段匹配的对象。如果您不想编写自己的验证函数,可以使用YupJoi等库。Formik 为Yup提供了一个特殊的 prop,名为,它会自动将 Yup 的验证错误转换为一个美观的对象,其键与相应的表单字段匹配。<Field /><Formik />validationSchema

dirty您可以通过诸如和 之类的 props 访问表单状态isSubmitting,如示例中所示,也可以通过 之类的事件处理程序handleSubmit访问表单状态。在示例中,通过调用handleReset作为 props 传入的函数来重置表单。

<Field />我喜欢和 的便捷使用<ErrorMessage />,但您也可以使用 HTML 表单控件或将自定义组件传递给<Field />。您还可以访问事件处理程序、表单值和验证状态作为 props。

KendoReact 表单

KendoReact Form是一个小巧快速的库,提供全面的无障碍支持,压缩和最小化后大小仅为6.2 kB。与本文列出的其他库相比,它的体积最小。它语法简洁,提供组件和 props 来访问表单状态,并全面支持 TypeScript。它支持字段级和表单级验证。让我们来看看一个使用 KendoReact 构建的类似用户数据表单。

import { useCallback } from "react";
import { Form, Field, FormElement } from "@progress/kendo-react-form";

const emailRegex = new RegExp(/\S+@\S+\.\S+/);
const emailValidator = (value) =>
  emailRegex.test(value) ? "" : "Please enter a valid email.";

const CustomCheckbox = (fieldRenderProps) => {
  const {
    validationMessage,
    visited,
    value,
    onChange,
    onFocus,
    onBlur,
    ...props
  } = fieldRenderProps;

  const onValueChange = useCallback(() => {
    onChange({ value: !value });
  }, [onChange, value]);

  return (
    <div onFocus={onFocus} onBlur={onBlur}>
      <label htmlFor={props.name}>{props.label}</label>
      <input
        type="checkbox"
        onChange={onValueChange}
        checked={value}
        id={props.id}
      />
      {visited && validationMessage && <span>{validationMessage}</span>}
    </div>
  );
};

const checkboxValidator = (value) =>
  value ? "" : "You must accept the terms and conditions before you proceed.";

const DataForm = () => {
  const handleSubmit = (dataItem) => alert(JSON.stringify(dataItem, null, 2));
  return (
    <Form
      initialValues={{ name: "", email: "" }}
      onSubmit={handleSubmit}
      validator={({ name, email, acceptedTerms }) => ({
        name: name ? "" : "Your name is required",
        email: emailValidator(email),
        acceptedTerms: checkboxValidator(acceptedTerms),
      })}
      render={(formRenderProps) => (
        <FormElement>
          <fieldset>
            <legend>Your Data</legend>
            <div>
              <label>Full Name </label>
              <Field name="name" component="input" />
              {formRenderProps.touched && formRenderProps.errors.name && (
                <span>{formRenderProps.errors.name}</span>
              )}
            </div>

            <div>
              <label>Email </label>
              <Field name="email" type="email" component="input" />
              {formRenderProps.touched && formRenderProps.errors.email && (
                <span>{formRenderProps.errors.email}</span>
              )}
            </div>
            <Field
              name="acceptedTerms"
              label={"Accept Terms"}
              component={CustomCheckbox}
            />
          </fieldset>
          <div>
            <button
              type="submit"
              disabled={!formRenderProps.modified}
              onClick={formRenderProps.onFormReset}
            >
              Reset
            </button>
            <button type={"submit"} disabled={!formRenderProps.allowSubmit}>
              Submit
            </button>
          </div>
        </FormElement>
      )}
    />
  );
};

export default DataForm;
Enter fullscreen mode Exit fullscreen mode

语法很简单,只需将一些 props 传递给<Form />组件即可。在示例中,我设置了initialValues, onSubmitprop 来处理表单提交和validator表单级验证。如果您选择使用字段级验证,则可以将validatorprops 传递给<Field /><Field />组件使用名称 prop 来存储输入的值,并可以渲染自定义组件或 HTML 元素,例如input。与 Formik 不同,在 Formik 中,您可以指定typeprop 并省略它,component然后它会渲染<input />,而 KendoReact 则要求您为 传递一个值component

您可以通过 props 访问表单状态和事件处理程序。在示例中,我使用“重置”onFormReset按钮重置表单,并知道何时禁用“提交”按钮。您还可以访问对象,其中包含每个验证失败字段的消息。allowSubmiterrors

使用 KendoReact Form 需要付费许可证。获得 KendoReact 许可证后,您可以使用一套组件来构建功能丰富、易于访问的 Web 应用程序,并获得快速的技术支持。您可以获得用于显示数据(例如网格)的各种组件、表单输入组件(例如复选框、下拉菜单)以及各种数据输入组件。

Label 包为您提供了可与表单元素进行视觉关联的组件,当表单元素获得焦点时,辅助技术将读取标签的内容。当与其关联的标签被点击时,表单输入元素将获得焦点。例如, Labels 包中的Error组件允许您显示错误消息。以下是如何结合使用 KendoReact Input、Label 和 Form 包的示例。

import { useState } from "react";
import { Label, Error } from "@progress/kendo-react-labels";
import { Input } from "@progress/kendo-react-inputs";
import { FormElement } from "@progress/kendo-react-form";

const App = () => {
  const [value, setValue] = useState();
  const editorId = "firstName";
  return (
    <FormElement style={{ maxWidth: 400 }}>
      <Label editorId={editorId}>First Name:&nbsp;</Label>
      <Input
        id={editorId}
        value={value}
        ariaDescribedBy={"firstNameError"}
        onChange={(e) => setValue(e.value)}
      />
      {!value && <Error id={"firstNameError"}>This field is required.</Error>}
    </FormElement>
  );
};
Enter fullscreen mode Exit fullscreen mode

KendoReact Form 文档非常详细,包括有关创建和设置具有可访问性支持的表单样式的表单设计指南。

React Hook 表单

React Hook Form是一个灵活的库,包含 hooks API 和非受控组件。它是开源的,拥有 17.3k 个 GitHub 星标,压缩和最小化后大小为9.1kB 。

该 API 与我提到的其他 API 略有不同。它支持 TypeScript 和 React Native,但与我提到的其他 API 不同,它没有用于包装表单的组件。您将使用useForm它提供的钩子来访问表单状态。我们来看一个例子。

import { useForm } from "react-hook-form";

export default function App() {
  const { register, handleSubmit, errors, reset, formState } = useForm();
  const { isDirty, isSubmitting } = formState;

  const onSubmit = (data) => alert(JSON.stringify(data, null, 2));

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <h1> Your Data</h1>
      <div>
        <label>Name</label>
        <input
          type="text"
          placeholder="Full Name"
          name="name"
          ref={register({ required: "Name Required " })}
        />
        <span>{errors.name?.message}</span>
      </div>
      <div>
        <label>Email</label>
        <input
          type="text"
          placeholder="Email"
          name="email"
          ref={register({
            required: "Email Required",
            pattern: { value: /^\S+@\S+$/i, message: "Invalid email address" },
          })}
        />
        <span>{errors.email?.message}</span>
      </div>
      <div>
        <label>Accept Terms</label>
        <input
          type="checkbox"
          placeholder="Accept Terms"
          name="acceptedTerms"
          ref={register({ required: true })}
        />
        {errors.acceptedTerms && <span>You must accepet the terms</span>}
      </div>

      <button type="button" onClick={reset} disabled={!isDirty || isSubmitting}>
        Reset
      </button>
      <input type="submit" disabled={isSubmitting} />
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

要使用此库,您需要调用useForm()钩子,它将返回用于管理表单状态的对象和函数。该handleSubmit函数将在表单提交时调用。它接受两个函数作为参数:如果表单验证成功,则使用表单数据调用第一个函数;如果验证失败,则调用第二个函数。

register函数允许您注册一个输入/选择元素 Ref 并提供验证规则。您可以在定义验证规则时指定错误消息,也可以跳过此操作。您可以在应用于输入的规则中看到不同的方法emailacceptedTerms如果您指定了错误消息,则可以在错误对象中通过message已验证字段的属性访问它。如果您想使用组件来渲染错误消息,就像您在 Formik 中看到的那样,您可以安装该@hookform/error-message包。有了它,您可以使用它来显示姓名和电子邮件的错误消息,如下所示:

import { ErrorMessage } from "@hookform/error-message";
// other necessary code ...
<ErrorMessage errors={errors} name="name" />

<ErrorMessage
  errors={errors}
  name="email"
  render={({ message }) => <p>{message}</p>}
/>
Enter fullscreen mode Exit fullscreen mode

React 最终形式

React Final Form是一个基于Final Form的订阅式表单状态管理库。它使用观察者模式,因此当表单状态发生变化时,只有需要更新的组件才会重新渲染。默认情况下,它会订阅所有更改,但如果您想要追求极致的极速体验,您可以指定只关注您关心的表单状态。

让我们看一下使用 Final Form 的语法。

import { Form, Field } from "react-final-form";

const DataForm = () => (
  <>
    <h1>Your Data</h1>
    <Form
      onSubmit={(values) => alert(JSON.stringify(values, 0, 2))}
      initialValues={{ acceptedTerms: true }}
      validate={(values) => {
        const errors = {};
        if (!values.name) {
          errors.name = "Required";
        }

        if (!values.acceptedTerms) {
          errors.acceptedTerms =
            "You must accept the terms and conditions before you proceed.";
        }

        if (!values.email) {
          errors.email = "Required";
        } else if (
          !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
        ) {
          errors.email = "Invalid email address";
        }
        return errors;
      }}
      render={({
        handleSubmit,
        form,
        submitting,
        pristine,
        values,
        errors,
        touched,
      }) => (
        <form onSubmit={handleSubmit}>
          <Field name="name">
            {({ input, meta }) => (
              <div>
                <label>Username</label>
                <input {...input} type="text" placeholder="Username" />
                {meta.error && meta.touched && <span>{meta.error}</span>}
              </div>
            )}
          </Field>
          <div>
            <label>Twitter Handle</label>
            <Field name="twitter" component="input" type="text" />
          </div>
          <Field name="email">
            {({ input, meta }) => (
              <div>
                <label>Email</label>
                <input {...input} type="email" />
                {meta.error && meta.touched && <span>{meta.error}</span>}
              </div>
            )}
          </Field>
          <div>
            <label>Accept Terms</label>
            <Field name="acceptedTerms" component="input" type="checkbox" />
            {touched.acceptedTerms && errors.acceptedTerms && (
              <span>{errors.acceptedTerms}</span>
            )}
          </div>
          <div>
            <button
              type="button"
              onClick={form.reset}
              disabled={submitting || pristine}
            >
              Reset
            </button>
            <button type="submit" disabled={submitting}>
              Submit
            </button>
          </div>
        </form>
      )}
    />
  </>
);

export default DataForm;
Enter fullscreen mode Exit fullscreen mode

示例中使用的 React Final Form 的两个组件是<Form /><Field /><Form />组件是​​ HTML 表单的包装器,用于管理表单状态和事件。您可以设置用于初始化表单状态、提交处理程序和表单级验证的 prop 的初始值。您还可以通过向组件传递 props 来validate进行字段级验证validate<Field />

您可以访问渲染属性,例如values表单数据、、handleSubmittouchederrors<Field />组件向表单注册一个字段,订阅该字段的状态,并通过渲染属性注入字段状态和回调函数(onBlur、onChange 和 onFocus)。我使用了一个子渲染函数,为姓名和电子邮件字段渲染一个带有关联输入和错误消息的标签。

与 Formik 和 React Hook Form 不同,它没有<ErrorMessage />组件。但是,你可以使用钩子轻松构建一个可在项目中复用的useField组件。

import { useField } from "react-final-form";

const ErrorMessage = ({ name }) => {
  const {
    meta: { error, touched },
  } = useField(name, { subscription: { error: true, touched: true } });
  return error && touched ? <span>{error}</span> : null;
};
Enter fullscreen mode Exit fullscreen mode

React Final Form 主要由Erik Rasmussen维护,他也是 Redux Form 的开发者。React Final Form 是他使用和维护 Redux Form 过程中积累的经验教训以及社区反馈的演进版本。它是开源的,拥有 6.3k 个 GitHub 星标,压缩并修改后大小为3.2 kB,Final Form 压缩后大小为 5.4 kB。

结论

列出的所有 React 表单库都速度很快,并且可以帮助您构建和管理高性能的复杂表单。Formik、KendoReact Form 和 React Final Form 提供了可用的组件,而 React Hook Form 则使用了钩子和不受控制的输入。我不太喜欢 React Hook Form 中使用的样式/语法,但如果你喜欢这种方法,那就使用它吧。

我更倾向于选择 React Final Form 或 KendoReact Form。我更喜欢它们的语法,而且<ErrorMessage />如果需要的话,我还可以构建组件。对我来说,使用 React Final Form 时,与其他方法相比,它所需的代码更少。

对于 KendoReact Form,我认为唯一的缺点是它不像其他产品那样免费。但是,获得整个KendoReact 库的许可证可以让你访问许多很棒的组件,从而构建一个功能丰富、性能卓越且易于访问的应用程序。你可以使用它来创建漂亮的主题/样式,并且所有组件都具有相同的外观和风格。你可以使用不同的输入组件,它们可以与 Form 包完美配合,从而创建易于访问的 React 表单。所有这些都在 KendoReact Forms 的“表单指南”中进行了详细记录,即使你不使用 KendoReact Forms,这也是一篇值得一读的文章。

就像我说的,如果这些库能满足您的需求,那么它们中的任何一个都是不错的选择。

链接:https://dev.to/pmbanugo/looking-for-the-best-react-form-library-in-2021-it-s-probably-on-this-list-e2h
PREV
Spring Boot 注解 通过注解学习 Spring Boot 我们为什么关注 Spring 注解 Spring 注解概述 主类注解 REST 端点注解 周期性任务注解 Bean 注解 依赖注入速记 Spring 托管组件注解 持久化注解 杂项注解 测试注解 总结
NEXT
JavaScript 中的数组分组(2024)