最近重刷了一下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都完美地满足了。
一,友好的文档
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 知识,阅读起来并不费劲。
这让我更有信心使用这个库 —— 即使项目中出现了它无法支持的小众功能需求,我也很可能可以修改源码自行解决问题。