前言
发现Formik是在我学习redux-form过程中从国外一篇博客上偶然发现的,看到作者的高度肯定后我立即转到github上,正如许多朋友所注意的,我发现其星数达8282,这个数字在github虽然不算很高,但是在探讨基于React技术开发跨平台表单这个主题的开源库角度来看,这个数字已经相当不错了。不自觉地,我对比了redux-form与Formik的几个数据,如下:
库 | 开源库的时间 | 星数 |
---|---|---|
redux-form | 3年以前 | 10210 |
Formik | 1年前 | 8282 |
几个不太肯定的结论(欢迎有兴趣的朋友一起讨论):
1,redux-form是目前基于React+Redux开发构建表单子项目时主要开源对象选择;
2,redux-form很可能是目前大多数React程序员心目中认为是高效率的选择方案;
3,我的预言是:在github上Formik在未来一年后星数很有可能会超过redux-form项目。
我作出上述猜测主要理由是,经过这段时间我redux-form学习,我发现要深度掌握redux-form实践应用并灵活地处理各种有关问题,需要花费相当的代价。尤其说明问题的是,在表单体积膨胀和数量急剧增加的情况下,系统的性能有可能受到严重的影响。现在我有了react-redux基础,并理解了redux-form应用原理后,感觉使用Formik开发React表单一下变得异常轻松愉快!
为了节约时间,本系列的几篇我会首先使用英语方式,再在后面的时间里逐篇翻译成中文。
Why not Redux-Form?
By now, you might be thinking, "Why didn‘t you just use Redux-Form?" Good question.
- According to our prophet Dan Abramov, form state is inherently ephemeral and local, so tracking it in Redux (or any kind of Flux library) is unnecessary
- Redux-Form calls your entire top-level Redux reducer multiple times ON EVERY SINGLE KEYSTROKE. This is fine for small apps, but as your Redux app grows, input latency will continue to increase if you use Redux-Form.
- Redux-Form is 22.5 kB minified gzipped (Formik is 7.8 kB)
My goal with Formik was to create a scalable, performant, form helper with a minimal API that does the really really annoying stuff, and leaves the rest up to you.
Use React for ephemeral state that doesn‘t matter to the app globally and doesn‘t mutate in complex ways. For example, a toggle in some UI element, a form input state. Use Redux for state that matters globally or is mutated in complex ways. For example, cached users, or a post draft.
Sometimes you‘ll want to move from Redux state to React state (when storing something in Redux gets awkward) or the other way around (when more components need to have access to some state that used to be local).
The rule of thumb is: do whatever is less awkward.
Dan Abramov(gaearon)
Working on @reactjs. Co-author of Redux and Create React App. Building tools for humans.From London, UK
Influences
Formik started by expanding on this little higher order component by Brent Jackson, some naming conventions from Redux-Form, and (most recently) the render props approach popularized by React-Motion and React-Router 4. Whether you have used any of the above or not, Formik only takes a few minutes to get started with.
Installation
Add Formik to your project.
npm i formik --save
示例
- Basics
- Sync Validation
- Building your own input primitives
- Working with 3rd-party inputs #1: react-select
- Working with 3rd-party inputs #2: Draft.js
- Accessing React lifecycle functions
- React Native
The gist
Formik keeps track of your form‘s state and then exposes it plus a few reusable methods and event handlers (handleChange, handleBlur, and handleSubmit) to your form via props. handleChange and handleBlur work exactly as expected--they use a name or id attribute to figure out which field to update.
There are two ways to use Formik:
withFormik(): A Higher-order Component (HoC) that accepts a configuration object
<Formik />: A React component with a render prop
Both do exactly the same thing and share the same internal implementation. They just differ in their respective style....
// Higher Order Component
import React from ‘react‘;
import { withFormik } from ‘formik‘;
// Our inner form component which receives our form‘s state and updater methods as props
const InnerForm = ({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting,
}) => (
<form onSubmit={handleSubmit}>
<input
type="email"
name="email"
onChange={handleChange}
onBlur={handleBlur}
value={values.email}
/>
{touched.email && errors.email && <div>{errors.email}</div>}
<input
type="password"
name="password"
onChange={handleChange}
onBlur={handleBlur}
value={values.password}
/>
{touched.password && errors.password && <div>{errors.password}</div>}
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</form>
);
// Wrap our form with the using withFormik HoC
const MyForm = withFormik({
// Transform outer props into form values
mapPropsToValues: props => ({ email: ‘‘, password: ‘‘ }),
// Add a custom validation function (this can be async too!)
validate: (values, props) => {
const errors = {};
if (!values.email) {
errors.email = ‘Required‘;
} else if (br/>!/^[A-Z0-9._%+-][email protected][A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)
) {
errors.email = ‘Invalid email address‘;
}
return errors;
},
// Submission handler
handleSubmit: (
values,
{
props,
setSubmitting,
setErrors / setValues, setStatus, and other goodies /,
}
) => {
LoginToMyApp(values).then(
user => {
setSubmitting(false);
// do whatevs...
// props.updateUser(user)
},
errors => {
setSubmitting(false);
// Maybe even transform your API‘s errors into the same shape as Formik‘s!
setErrors(transformMyApiErrors(errors));
}
);
},
})(InnerForm);
// Use <MyForm /> anywhere
const Basic = () => (
<div>
<h1>My Form</h1>
<p>This can be anywhere in your application</p>
<MyForm />
</div>
);
export default Basic;
// Render Prop
import React from ‘react‘;
import { Formik } from ‘formik‘;
const Basic = () => (
<div>
<h1>My Form</h1>
<p>This can be anywhere in your application</p>
{/*
The benefit of the render prop approach is that you have full access to React‘s
state, props, and composition model. Thus there is no need to map outer props
to values...you can just set the initial values, and if they depend on props / state
then--boom--you can directly access to props / state.
The render prop accepts your inner form component, which you can define separately or inline
totally up to you:
<Formik render={props => <form>...</form>}>
<Formik component={InnerForm}>
<Formik>{props => <form>...</form>}</Formik>
(identical to as render, just written differently)
/}
<Formik
initialValues={{
email: ‘‘,
password: ‘‘,
}}
validate={values => {
// same as above, but feel free to move this into a class method now.
let errors = {};
if (!values.email) {
errors.email = ‘Required‘;
} else if (br/>!/^[A-Z0-9._%+-][email protected][A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)
) {
errors.email = ‘Invalid email address‘;
}
return errors;
}}
onSubmit={(
values,
{ setSubmitting, setErrors / setValues and other goodies */ }
) => {
LoginToMyApp(values).then(
user => {
setSubmitting(false);
// do whatevs...
// props.updateUser(user)
},
errors => {
setSubmitting(false);
// Maybe transform your API‘s errors into the same shape as Formik‘s
setErrors(transformMyApiErrors(errors));
}
);
}}
render={({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting,
}) => (
<form onSubmit={handleSubmit}>
<input
type="email"
name="email"
onChange={handleChange}
onBlur={handleBlur}
value={values.email}
/>
{touched.email && errors.email && <div>{errors.email}</div>}
<input
type="password"
name="password"
onChange={handleChange}
onBlur={handleBlur}
value={values.password}
/>
{touched.password && errors.password && <div>{errors.password}</div>}
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</form>
)}
/>
</div>
);
export default Basic;
Complementary Packages
As you can see above, validation is left up to you. Feel free to write your own validators or use a 3rd party library. Personally, I use Yup for object schema validation. It has an API that‘s pretty similar Joi / React PropTypes but is small enough for the browser and fast enough for runtime usage. Because I ?? Yup sooo much, Formik has a special config option / prop for Yup called validationSchema which will automatically transform Yup‘s validation errors into a pretty object whose keys match values and touched. Anyways, you can install Yup from npm...
npm install yup --save
(未完待续...)
参考资料
1.https://github.com/jaredpalmer/formik
2.http://www.lizhe.name/node/252
3.https://keyholesoftware.com/2017/10/23/the-joy-of-forms-with-react-and-formik/
4.
原文地址:http://blog.51cto.com/zhuxianzhong/2151661