最近重刷了一下React官方文档关于表单的内容,在结尾处,官方讨论成熟的react社区表单库时 钦点 了Formik,引起了我的注意。

If you’re looking for a complete solution including validation, keeping track of the visited fields, and handling form submission,Formikis one of the popular choices

React官方钦点库,不学留着当寒假作业吗?

通读了一遍 Formik 的官方文档,上手在项目中试用了一下,大喜! —— ”这个表单库也太优秀了吧!“

我个人认为优秀的第三方库需要符合四个标准,Formik都完美地满足了。




vform表单设计器 拖组件报错_html


一,友好的文档

Formik 的官方文档 的友好程度简直让人惊喜。

首先是 overview 简要阐述了Formik作者的创作动机,基本的安装流程,还有 hello world的试玩环境(playground),以及几段基础的示例代码

但是更令我惊喜的是第二章 tutorial 。这一章,以创建一个完整且复杂的 新闻订阅注册表单 为例,一步步地“手把手”地教读者使用 Formik。由最基础的表单功能,到验证功能,到只追踪 修改过的项;接着,又以优化代码(减少样板代码)为由进一步介绍了 Formik 多种写法;最后还介绍了如何自定义表单组件。

第二章我认为是非常优秀且标准的教学文档(tutorial),文档内容由浅入深,层层递进,而且兼具情景用例和说明。

我在了解Formik之前,其实心中有好几个问题,在阅读文档时都被一个个解决了,这种感觉让我非常舒服,充分说明了文档编写时已经考虑好了大部分用户的潜在问题。

二,保持更新,紧随社区趋势

有不少库的作者,渐渐降低了维护项目的频率,甚至放弃了追随社区的趋势,比如 formsy-react (我所在项目之前使用的表单库)这个库看起来并不打算适配 React Hooks —— 而众所周知,React社区正往 React Hooks 方向不断发展。

对比之下,Formik 在 tutorial 中的示例代码中,一出场就使用了 Hooks —— 可以说非常“时髦”了


// Pass the useFormik() hook initial form values and a submit function that will
  // be called when the form is submitted
  const formik = useFormik({
    initialValues: {
      email: '',
    },
    onSubmit: values => {
      alert(JSON.stringify(values, null, 2));
    },
  });


我所负责的前端项目也在不断重构、演进,之后一定会使用越来越多的函数式组件 和 Hooks, 看到文档这块时我立刻就有种感觉“Formik 似乎会是个不错的新表单库选择”。

三,足够灵活

React 一直以来都是以灵活著称,这种设计思想深入React,也应该深入 React 开发者。选择第三方库,我也会考虑是否足够灵活。

Formik 在多个方面都体现了它的灵活性:

  • Formik对表单的操作符合了React官方文档推荐的方式 —— 受控组件的形式,这种形式好处在于让 React 的 state 成为了single source of truth,避免歧义;此外,获取当前表单值也更加直接和安全。对比之下,另外一些表单库如 react-hook-form 拥抱了非受控组件的形式,我个人发现欠缺了一定的灵活性(言词很难表述清楚,有兴趣的读者可以自行尝试两种方案;评论区里也有讨论,欢迎加入)。
    我专门写了一篇相关文章,欢迎阅读


FreewheelLee:我果然还是喜欢白一点的 —— React开发中的白盒表单vs黑盒表单zhuanlan.zhihu.com


  • 自定义的表单验证方式:Formik 的表单验证逻辑完全开放给用户,用户可以自定义,比如
// A custom validation function. This must return an object
// which keys are symmetrical to our values/initialValues
const validate = values => {
  const errors = {};
  if (!values.firstName) {
    errors.firstName = 'Required';
  } else if (values.firstName.length > 15) {
    errors.firstName = 'Must be 15 characters or less';
  }

  if (!values.email) {
    errors.email = 'Required';
  } else if (!isValidCompanyInternalEmail(values.email)) {
    // 是不是符合公司内部邮件的格式
    errors.email = 'Invalid email address';
  }

  return errors;
};


Formik 表单验证还天然支持社区非常流行的 Yup 库


const formik = useFormik({
    initialValues: {
      firstName: '',
      lastName: '',
      email: '',
    },
    validationSchema: Yup.object({
      firstName: Yup.string()
        .max(15, 'Must be 15 characters or less')
        .required('Required'),
      lastName: Yup.string()
        .max(20, 'Must be 20 characters or less')
        .required('Required'),
      email: Yup.string()
        .email('Invalid email address')
        .required('Required'),
    }),
    onSubmit: values => {
      alert(JSON.stringify(values, null, 2));
    },
  });


  • 支持多种使用方式: 除了使用最直接的 hook,Formik 利用 React Context 创建了一些组件如 <Formik />, <Form />, <Field /> 从而达到了简化代码的目的,如
<Formik
      initialValues={{ firstName: '', lastName: '', email: '' }}
      validationSchema={Yup.object({
        firstName: Yup.string()
          .max(15, 'Must be 15 characters or less')
          .required('Required'),
        lastName: Yup.string()
          .max(20, 'Must be 20 characters or less')
          .required('Required'),
        email: Yup.string()
          .email('Invalid email address')
          .required('Required'),
      })}
      onSubmit={(values, { setSubmitting }) => {
        setTimeout(() => {
          alert(JSON.stringify(values, null, 2));
          setSubmitting(false);
        }, 400);
      }}
    >
      <Form>
        <label htmlFor="firstName">First Name</label>
        <Field name="firstName" type="text" />
        <ErrorMessage name="firstName" />
        <label htmlFor="lastName">Last Name</label>
        <Field name="lastName" type="text" />
        <ErrorMessage name="lastName" />
        <label htmlFor="email">Email Address</label>
        <Field name="email" type="email" />
        <ErrorMessage name="email" />
        <button type="submit">Submit</button>
      </Form>
    </Formik>


  • 灵活支持第三方的组件库,比如业界非常流行的 Material UI 和 Antd 。除了有专门的衍生库(详见文档 https://jaredpalmer.com/formik/docs/3rd-party-bindings ),Formik 由于本身设计得非常灵活(受控组件),直接衔接第三方库也不是难事,如对 Material UI 可以这么用,非常自然
<TextField
            error={!_.isEmpty(formik.errors.subject)}
            helperText={_.isEmpty(formik.errors.subject) ? "" : formik.errors.subject}
            fullWidth
            label="Subject"
            name="subject"
            variant="outlined"
            className="ml-16 mt-16 mb-8 flex-grow"
            onChange={formik.handleChange}
            value={formik.values.subject}
            required
            focused
        />


对比之下,react-hook-form 的解决方案略显僵硬。

四,高质量源码

在确定 Formik 在应用层面上足够优秀后,我又去看了它的源码,比如最核心的 Formik.tsx

代码非常规范,干净,没那么多花里胡哨的东西,只要有足够的 React 和 React Hooks 知识,阅读起来并不费劲。

这让我更有信心使用这个库 —— 即使项目中出现了它无法支持的小众功能需求,我也很可能可以修改源码自行解决问题