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,那么你就无法使用它。

相关推荐
bysking14 分钟前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓30 分钟前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_41133 分钟前
无网络安装ionic和运行
前端·npm
理想不理想v35 分钟前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云44 分钟前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js
微信:137971205871 小时前
web端手机录音
前端
齐 飞1 小时前
MongoDB笔记01-概念与安装
前端·数据库·笔记·后端·mongodb
神仙别闹1 小时前
基于tensorflow和flask的本地图片库web图片搜索引擎
前端·flask·tensorflow
GIS程序媛—椰子2 小时前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js
DogEgg_0012 小时前
前端八股文(一)HTML 持续更新中。。。
前端·html