1. 程式人生 > >使用Formik輕松開發更高質量的React表單(一)入門

使用Formik輕松開發更高質量的React表單(一)入門

NPU pen node ive ble plus one lex imp

技術分享圖片

前言


發現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.


  1. 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
  2. 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.
  3. 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 (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)
br/>!/^[A-Z0-9._%+-]+@[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:

  • &lt;Formik render={props =&gt; &lt;form&gt;...&lt;/form&gt;}&gt;
  • &lt;Formik component={InnerForm}&gt;
  • &lt;Formik&gt;{props =&gt; &lt;form&gt;...&lt;/form&gt;}&lt;/Formik&gt; (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 (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)
    br/>!/^[A-Z0-9._%+-]+@[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.

使用Formik輕松開發更高質量的React表單(一)入門