React Final Form。一个用于实现更高性能的表单的库

1,963 阅读6分钟

有许多库可用于创建和管理表单。在React中,表单一直是有点复杂的。表单库的目的是在不影响性能的情况下简化表单管理。

在这篇文章中,我们将看一下React Final Form,一个流行的表单管理库。我们将介绍React Final Form是如何工作的,将它与一些竞争对手进行比较,最后,用一个相关的例子来测试它。让我们开始吧!

什么是React Final Form?

React Final Form 是一个用核心 JavaScript 编写的轻量级表单库,它作为Final Form 的一个封装,是一个表单状态管理库。

React Final Form使用观察者设计模式,其中的组件订阅特定的事件。而不是整个表单重新渲染,只有被订阅的字段才会重新渲染。

让我们来看看React Final Form的一些主要特点。

最小的捆绑尺寸

React Final Form是对Final Form库的简单包装。它没有任何依赖性,而且是用纯JavaScript编写的,这使得它与框架无关。React Final Form的捆绑包大小只有3.2kB,而且是经过压缩的。

简单性

由于其简单的表单状态管理,React Final Form强调为必要的功能编写代码,而不是为简单的表单编写不必要的代码。React Final Form的设计是高度模块化的,使其成为许多使用案例的完美选择。

高效能

虽然重新渲染在小型表单中不是什么大问题,但随着表单大小的增加,我们看到每次重新渲染的性能都会有明显的滞后。由于其基于订阅的模式,React Final Form只重新渲染需要的字段,防止了延迟。

现在我们知道了React Final Form的基本情况,让我们来看看Formik,一个类似的库,来看看两者的比较。

与Formik的比较

Formik是一个协助开发者编写React代码的三个领域的库:在表单状态中获取数值,验证和错误信息,以及表单提交。

受欢迎程度和社区

让我们看看这两个库在npm上的趋势,以衡量人气和社区规模。我们看到,在过去的六个月里,Formik每周获得的下载量高于React Final Form。

Formik React Final Form Npm Downloads

在GitHub上,React Final Form有6.6万颗星,而Formik有27.7万颗星。Formik显然有一个更大的在线社区,然而,这两个库都有大量的线程和论坛,这意味着你应该能够得到社区的支持。

从下面的截图中可以看出,这两个库的更新都很频繁。

React Final Form Formik Updates

React Final Form目前在GitHub上的开放问题比Formik少,但如果该库越来越受欢迎,这种情况在未来可能会改变。

尺寸和依赖性

Formik的bundle大小为13kB,比React Final Form的bundle大小为3.2kB大。

下面,我们可以看到两个库的捆绑构成。React Final Form的依赖关系较少,减少了更新时库破裂的机会。

React Final Form Bundle Composition

Formik Bundle Composition

设置React Final Form

让我们通过启动我们自己的项目来测试React Final Form的功能。设置一个React项目,通过运行以下命令安装React Final Form库。

npm install --save final-form react-final-form

一旦库安装完毕,从库中导入主要组件,如下所示。

import { Form, Field } from 'react-final-form'

注意,在上面的代码片段中,我们导入了两个组件,FormFieldForm 是父组件,它为我们的表单管理采取了所有的道具,而Field 则包装了HTML元素,以创建一个独立的Final Form组件。由Field 创建的组件有自己的状态,由Form 标签管理。

让我们在React Final Form中编写一个简单的输入表单的代码。我们的代码包含firstNamelastName 的输入字段。我们还添加了一个submit 按钮。

/* eslint-disable jsx-a11y/accessible-emoji */
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 - Simple Example</h1>

    <Form
      onSubmit={onSubmit}
      initialValues={{ firstname: '', lastname :''}}
      render={({ handleSubmit, form, submitting, pristine, values }) => (
        <form onSubmit={handleSubmit}>
          <div>
            <label>First Name</label>
            <Field
              name="firstName"
              component="input"
              type="text"
              placeholder="First Name"
            />
          </div>
          <div>
            <label>Last Name</label>
            <Field
              name="lastName"
              component="input"
              type="text"
              placeholder="Last Name"
            />
          </div>

          <div className="buttons">
            <button type="submit" disabled={submitting || pristine}>
              Submit
            </button>
            <button
              type="button"
              onClick={form.reset}
              disabled={submitting || pristine}
            >
              Reset
            </button>
          </div>
        </form>
      )}
    />
  </Styles>
)

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

启动服务器给我们提供了以下输出。

Starting React Final Form Server Output

我们正在调用两个日志,一个来自Form ,一个来自Field 。让我们试着在FirstName 中输入sam ,看看会发生什么!

Call Field Form Logs React Final Form

React Final Form Single Render

注意,Form 只渲染了一次。Field 组件显示了独立的行为,因为它渲染的次数与输入的字符数相同。在React中,我们应该始终以较少的重新渲染次数为目标,以避免随着表单大小的增加而出现延迟。

我们使用了一个渲染道具,它使我们能够从Form 组件中访问不同的道具。请看下面我们的例子的最终输出。

React Final Form Example Form

现在我们已经看到了React Final Form是如何工作的,让我们用Formik运行同样的例子。

设置Formik

像以前一样,我们将设置一个简单的表单,有一个firstName 的字段,一个lastName 的字段,以及一个提交按钮。让我们把我们的表单称为Client Profile

import React from "react";
import ReactDOM from "react-dom";
import { Formik, Form, Field } from "formik";


// Messages
export default function App() {
  return (
    <Formik
      initialValues={{
        firstname: "",
        lastname: "",
      }}
      onSubmit={(values, { setSubmitting }) => {
        setTimeout(() => {
          console.log(JSON.stringify(values, null, 2));
          setSubmitting(false);
        }, 400);
      }}
    >
      {({ errors, touched, isValidating }) => (
        <div className="container">
          <div>
            <h3>Client Profile</h3>
          </div>
          <div>
            <Form>
              {console.log(“Render”)}
              <div>
                <Field
                  type="text"
                  placeholder="First Name"
                  name="firstname"
                />
              </div>
              <div>
                <Field

                  type="text"
                  placeholder="lastname"
                  name="name" 
                />
              </div>
            <button type="submit">Submit</button>
            </Form>
          </div>
        </div>
      )}
    </Formik>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

在第一次渲染时,我们的表单看起来像下面的图片。

Formik First Render Output

我们在Form 标签中放置了一个日志,它将记录渲染的情况。让我们在input 字段中输入Sam 。我们得到下面的输出。

Formik Log Form Tag

请注意,当我们输入一个输入值时,表单总共重新渲染了九次,而React Final Form的渲染次数只有一次。让我们深入考虑这些例子。

subscription prop

在React Final Form中,Form 组件采取了subscription 道具,它实现了观察者设计模式,并导致更少的渲染。subscription 道具类似于 [useEffect](https://reactjs.org/docs/hooks-effect.html)钩子,因为它观察传递给它的值,并在它们发生变化时重新渲染。

在上面的Formik代码块中,我们没有在prop里面传递值。相反,Form 正在观察{submitting || pristine} 的变化。

验证

React Final Form提供两种类型的验证:表单级验证和字段级验证。通过字段级验证,你可以在Field 被改变时运行验证。在表单级验证中,验证测试是在提交Form 的时候运行的。

Formik有一个类似的验证机制,使用一个 validationSchema.因此,这两个库在这方面是平等的。

总结

React Final Form处理表单的范式与其他库不同。它通过使用观察者设计模式有效地处理了不同库的重新渲染问题。

React Final Form不仅在尺寸上比Formik小,而且速度也更快。因此,如果你的目标是在不影响性能的情况下为你的React应用程序创建大型复杂的表单,React Final Form是最好的选择。

The postReact Final Form:更高性能的表单库首次出现在LogRocket博客上。