React表单:formik、final-form和react-hook-form

表单无处不在,它是每个网站的必备部分。在用React构建web应用时,处理表单是不可避免的。

你可以选择自己的方式来处理,或者选择社区中现成的库。然而,当你选择一个第三方库时,你会立即面临一个问题:有太多的库可供选择。

选择一个库是一件棘手的事情,随意选择一个GitHub上星标多的库并不总是明智的。你肯定需要阅读文档、代码示例,并多次尝试,才能确定这是你想要用于自己项目的库。

对于处理表单,我们有以下选择:

库名称 GitHub星标 下载次数 体积
formik 23.4K 868k/周 7.22 kB
final-form 2.5K 222k/周 5.1 kB
react-Form 1.9K 12k/周 4.3 kB
react-hook-form 12.5K 270k/周 8.67 kB
redux-form 12.3K 389k/周 26.4 kB

在这里中,我们将比较下载次数最多的三个库:formik、final-form和react-hook-form。

对于redux-form,我们可以忽略它,毕竟已经是2020年了,没有人会再把每个输入的keystore存储在Redux中,这极其影响性能。而且该库的作者也建议大家转向使用final-form。

使用方式比较

Formik

在GitHub星标数量、下载次数方面,formik是使用人数最多的库,星标最多,下载次数也最多。

老实说,我不喜欢formik的口号"无需含泪构建表单",因为在React中构建表单和验证从未过于复杂。

使用formik,你需要在组件的render部分直接渲染Formik的Form组件:

jsx 复制代码
import React from "react";
import { Formik } from "formik";

const Basic = () => (
  <div>
    <h1>在你的应用任何地方!</h1>
    <Formik
      initialValues={{ email: "", password: "" }}
      validate={(values) => {
        const errors = {};
        if (!values.email) {
          errors.email = "必填";
        } else if (
          !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
        ) {
          errors.email = "无效的电子邮件地址";
        }
        return errors;
      }}
      onSubmit={(values, { setSubmitting }) => {
        setTimeout(() => {
          alert(JSON.stringify(values, null, 2));
          setSubmitting(false);
        }, 400);
      }}
    >
      {({
        values,
        errors,
        touched,
        handleChange,
        handleBlur,
        handleSubmit,
        isSubmitting,
        /* 和其他好东西 */
      }) => (
        <form onSubmit={handleSubmit}>
          <input
            type="email"
            name="email"
            onChange={handleChange}
            onBlur={handleBlur}
            value={values.email}
          />
          {errors.email && touched.email && errors.email}
          <input
            type="password"
            name="password"
            onChange={handleChange}
            onBlur={handleBlur}
            value={values.password}
          />
          {errors.password && touched.password && errors.password}
          <button type="submit" disabled={isSubmitting}>
            提交
          </button>
        </form>
      )}
    </Formik>
  </div>
);

export default Basic;

上述代码相当长,并且有一些缺点:

  • 你必须在组件内直接渲染Formik组件,这使得你的渲染部分相对混乱。
  • 你必须手动将handleChange、handleBlur等函数映射到输入元素上,这项工作相当耗时。不过,你可以使用formik的Field或ErrorMessage组件来节省时间。
  • Formik没有内置验证,你需要自己编写函数来验证表单值,或者使用像yup这样的库来支持验证。
  • Formik不支持完整的hook,尽管你可以使用useFormik hook,但使用这个hook时,Field、ErrorMessage、FieldArray等组件将无法工作。

Final-Form

final-form由redux-form的作者编写,因此相当有名。

创建一个final-form表单如下:

jsx 复制代码
import React from "react";
import { render } from "react-dom";
import Styles from "./Styles";
import { Form, Field } from "react-final-form";

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

const onSubmit = async (values) => {
  await sleep(300);
  window.alert(JSON.stringify(values, 0, 2));
};

const App = () => (
  <Styles>
    <h1>React Final Form示例</h1>
    <h2>密码/确认验证</h2>
    <a
      href="https://final-form.org/react"
      target="_blank"
      rel="noopener noreferrer"
    >
      阅读文档
    </a>
    <Form
      onSubmit={onSubmit}
      validate={(values) => {
        const errors = {};
        if (!values.username) {
          errors.username = "必填";
        }
        if (!values.password) {
          errors.password = "必填";
        }
        if (!values.confirm) {
          errors.confirm = "必填";
        } else if (values.confirm !== values.password) {
          errors.confirm = "必须匹配";
        }
        return errors;
      }}
      render={({ handleSubmit, form, submitting, pristine, values }) => (
        <form onSubmit={handleSubmit}>
          <Field name="username">
            {({ input, meta }) => (
              <div>
                <label>用户名</label>
                <input {...input} type="text" placeholder="用户名" />
                {meta.error && meta.touched && <span>{meta.error}</span>}
              </div>
            )}
          </Field>
          <Field name="password">
            {({ input, meta }) => (
              <div>
                <label>密码</label>
                <input {...input} type="password" placeholder="密码" />
                {meta.error && meta.touched && <span>{meta.error}</span>}
              </div>
            )}
          </Field>
          <Field name="confirm">
            {({ input, meta }) => (
              <div>
                <label>确认</label>
                <input {...input} type="password" placeholder="确认" />
                {meta.error && meta.touched && <span>{meta.error}</span>}
              </div>
            )}
          </Field>
          <div className="buttons">
            <button type="submit" disabled={submitting}>
              提交
            </button>
            <button
              type="button"
              onClick={form.reset}
              disabled={submitting || pristine}
            >
              重置
            </button>
          </div>
          <pre>{JSON.stringify(values, 0, 2)}</pre>
        </form>
      )}
    />
  </Styles>
);

render(<App />, document.getElementById("root"));

在我看来,使用final-form的方式与formik类似,但在渲染Form组件后,你必须使用Field组件来传递final-form的props到你的输入元素。

因此,将final-form与react-select等其他React组件集成可能会有些困难。

此外,final-form没有内置验证,你必须自己编写函数来验证表单数据。

Final-form也不支持joi或yup,你必须自己找到方法让final-form能够使用yup,如下所示:链接

React-Hook-Form

第一次看到是在reddit上(www.reddit.com/r/reactjs)上...%25E4%25B8%258A%25EF%25BC%258C%25E5%25BD%2593%25E6%2597%25B6%25E9%25A1%25B9%25E7%259B%25AE%25E4%25B9%259F%25E9%259C%2580%25E8%25A6%2581%25E5%25A4%2584%25E7%2590%2586%25E8%25A1%25A8%25E5%258D%2595%25EF%25BC%258C%25E6%2588%2591%25E8%2587%25AA%25E5%25B7%25B1%25E5%2586%2599%25E8%25A1%25A8%25E5%258D%2595%25E9%259D%259E%25E5%25B8%25B8%25E8%2580%2597%25E6%2597%25B6%25EF%25BC%258C%25E6%2589%2580%25E4%25BB%25A5%25E6%2588%2591%25E5%2586%25B3%25E5%25AE%259A%25E5%25B0%259D%25E8%25AF%2595%25E4%25BD%25BF%25E7%2594%25A8react-hook-form%25E3%2580%2582 "https://www.reddit.com/r/reactjs)%E4%B8%8A%EF%BC%8C%E5%BD%93%E6%97%B6%E9%A1%B9%E7%9B%AE%E4%B9%9F%E9%9C%80%E8%A6%81%E5%A4%84%E7%90%86%E8%A1%A8%E5%8D%95%EF%BC%8C%E6%88%91%E8%87%AA%E5%B7%B1%E5%86%99%E8%A1%A8%E5%8D%95%E9%9D%9E%E5%B8%B8%E8%80%97%E6%97%B6%EF%BC%8C%E6%89%80%E4%BB%A5%E6%88%91%E5%86%B3%E5%AE%9A%E5%B0%9D%E8%AF%95%E4%BD%BF%E7%94%A8react-hook-form%E3%80%82")

使用react-hook-form的方式如下:

jsx 复制代码
import React from "react";
import { useForm } from "react-hook-form";

export default function App() {
  const { register, handleSubmit, watch, errors } = useForm();
  const onSubmit = data => console.log(data);

  console.log(watch("example")); // 通过传递名称来监视输入值

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="example" defaultValue="test" ref={register} />

      <input name="exampleRequired" ref={register({ required: true })} />
      {errors.exampleRequired && <span>这个字段是必填的</span>}

      <input type="submit" />
    </form>
  );
}

在这三个库中,我认为react-hook-form是最容易使用的。你只需要使用useForm来创建表单,同时使用register来将输入注册到react-hook-form。

当你将react-hook-form与react-select等其他表单输入库一起使用时,也非常简单,你可以使用setValue函数将该组件的值传递给react-hook-form。

React-hook-form还提供了一些内置函数来验证表单内容:

jsx 复制代码
import React from "react";
import { useForm } from "react-hook-form";

export default function App() {
  const { register, handleSubmit } = useForm();
  const onSubmit= data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        name="firstName"
        ref={register({ required: true, maxLength: 20 })}
      />
      <input
        name="lastName"
        ref={register({ pattern: /^[A-Za-z]+$/i })}
      />
      <input
        name="age"
        type="number"
        ref={register({ min: 18, max: 99 })}
      />
      <input type="submit" />
    </form>
  );
}

同时,你也可以使用像yup这样的库来验证表单内容。

react-hook-form 基于非受控组件,这意味着你不需要通过状态和 onChange 事件来更新输入值。相反,你只需要设置 defaultValue。

根据react-hook-form的官网,由于它限制了不必要的组件重新渲染次数,并且具有最低的挂载时间,因此它具有最佳的性能。

另外它体积小(压缩后仅8.6 kB),而且没有外部依赖,使得应用的加载速度更快,同时也减少了项目的复杂性。

结论

react-hook-form是目前最容易使用的库,同时它的文档也非常好。在我看来,react-hook-form唯一的限制是它只能与函数组件一起使用。因此,如果你的项目仍然在使用旧版本的React,那么你就无法使用它。

相关推荐
卸任8 小时前
Vibe coding工具使用心得
前端·ai编程
brien99 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance9 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M9 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写10 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李10 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易10 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马10 小时前
前沿视界
前端
kyriewen10 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易10 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员