使用Formik轻松开发更高质量的React表单(二)使用指南
基础
Imagine you want to build a form that lets you edit user data. However,your user API has nested objects like so. { id: string,email: string,social: { facebook: string,twitter: string,// ... } } When we are done we want our dialog to accept just a user,updateUser,and onClose props. // User.js import React from ‘react‘; import Dialog from ‘MySuperDialog‘; import { Formik } from ‘formik‘; const EditUserDialog = ({ user,onClose }) => { return ( <Dialog onClose={onClose}> <h1>Edit User</h1> <Formik initialValues={user /** { email,social } */} onSubmit={(values,actions) => { CallMyApi(user.id,values).then( updatedUser => { actions.setSubmitting(false); updateUser(updatedUser),onClose(); },error => { actions.setSubmitting(false); actions.setErrors(transformMyAPIErrorToAnObject(error)); } ); }} render={({ values,errors,touched,handleBlur,handleChange,handleSubmit,isSubmitting,}) => ( <form onSubmit={handleSubmit}> <input type="email" name="email" onChange={handleChange} onBlur={handleBlur} value={values.email} /> {errors.email && touched.email && <div>{errors.email}</div>} <input type="text" name="social.facebook" onChange={handleChange} onBlur={handleBlur} value={values.social.facebook} /> {errors.social && errors.social.facebook && touched.facebook && <div>{errors.social.facebook}</div>} <input type="text" name="social.twitter" onChange={handleChange} onBlur={handleBlur} value={values.social.twitter} /> {errors.social && errors.social.twitter && touched.twitter && <div>{errors.social.twitter}</div>} <button type="submit" disabled={isSubmitting}> Submit </button> </form> )} /> </Dialog> ); }; To make writing forms less verbose. Formik comes with a few helpers to save you key strokes.
// EditUserDialog.js import React from ‘react‘; import Dialog from ‘MySuperDialog‘; import { Formik,Field,Form } from ‘formik‘; const EditUserDialog = ({ user,error => { actions.setSubmitting(false); actions.setErrors(transformMyAPIErrorToAnObject(error)); } ); }} render={({ errors,isSubmitting }) => ( <Form> <Field type="email" name="email" /> {errors.email && touched.social.email && <div>{errors.email}</div>} <Field type="text" name="social.facebook" /> {errors.social.facebook && touched.social.facebook && <div>{errors.social.facebook}</div>} <Field type="text" name="social.twitter" /> {errors.social.twitter && touched.social.twitter && <div>{errors.social.twitter}</div>} <button type="submit" disabled={isSubmitting}> Submit </button> </Form> )} /> </Dialog> ); }; React NativeFormik is 100% compatible with React Native and React Native Web. However,because of differences between ReactDOM‘s and React Native‘s handling of forms and text input,there are two differences to be aware of. This section will walk you through them and what I consider to be best practices. Before going any further,here‘s a super minimal gist of how to use Formik with React Native that demonstrates the key differences: // Formik x React Native example import React from ‘react‘; import { Button,TextInput,View } from ‘react-native‘; import { withFormik } from ‘formik‘; const enhancer = withFormik({ /*...*/ }); const MyReactNativeForm = props => ( <View> <TextInput onChangeText={props.handleChange(‘email‘)} onBlur={props.handleBlur(‘email‘)} value={props.values.email} /> <Button onPress={props.handleSubmit} title="Submit" /> </View> ); export default enhancer(MyReactNativeForm); As you can see above,the notable differences between using Formik with React DOM and React Native are: Formik‘s props.handleSubmit is passed to a <Button onPress={...} /> instead of HTML <form onSubmit={...} /> component (since there is no <form /> element in React Native). Write your own class wrapper around the custom input element // FormikReactNativeTextInput.js import * as React from ‘react‘; import { TextInput } from ‘react-native‘; export default class FormikReactNativeTextInput extends React.Component { handleChange = (value: string) => { // remember that onChangeText will be Formik‘s setFieldValue this.props.onChangeText(this.props.name,value); }; render() { // we want to pass through all the props except for onChangeText const { onChangeText,...otherProps } = this.props; return ( <TextInput onChangeText={this.handleChange} {...otherProps} // IRL,you should be more explicit when using TS /> ); } } Then you could just use this custom input as follows: // MyReactNativeForm.js import { View,Button } from ‘react-native‘; import TextInput from ‘./FormikReactNativeTextInput‘; import { Formik } from ‘formik‘; const MyReactNativeForm = props => ( <View> <Formik onSubmit={(values,actions) => { setTimeout(() => { console.log(JSON.stringify(values,null,2)); actions.setSubmitting(false); },1000); }} render={props => ( <View> <TextInput name="email" onChangeText={props.setFieldValue} value={props.values.email} /> <Button title="submit" onPress={props.handleSubmit} /> </View> )} /> </View> ); export default MyReactNativeForm; Using Formik with TypeScriptTypeScript TypesThe Formik source code is written in TypeScript,so you can rest assured that types will always be up to date. As a mental model,Formik‘s types are very similar to React Router 4‘s <Route>. Render props (<Formik /> and <Field />) import * as React from ‘react‘; import { Formik,FormikProps,Form,FieldProps } from ‘formik‘; interface MyFormValues { firstName: string; } export const MyApp: React.SFC<{} /* whatever */> = () => { return ( <div> <h1>My Example</h1> <Formik initialValues={{ firstName: ‘‘ }} onSubmit={(values: MyFormValues) => alert(JSON.stringify(values))} render={(formikBag: FormikProps<MyFormValues>) => ( <Form> <Field name="firstName" render={({ field,form }: FieldProps<MyFormValues>) => ( <div> <input type="text" {...field} placeholder="First Name" /> {form.touched.firstName && form.errors.firstName && form.errors.firstName} </div> )} /> </Form> )} /> </div> ); }; withFormik()import React from ‘react‘; import * as Yup from ‘yup‘; import { withFormik,FormikErrors,Field } from ‘formik‘; // Shape of form values interface FormValues { email: string; password: string; } interface OtherProps { message: string; } // You may see / user InjectedFormikProps<OtherProps,FormValues> instead of what comes below. They are the same--InjectedFormikProps was artifact of when Formik only exported an HOC. It is also less flexible as it MUST wrap all props (it passes them through). const InnerForm = (props: OtherProps & FormikProps<FormValues>) => { const { touched,message } = props; return ( <Form> <h1>{message}</h1> <Field type="email" name="email" /> {touched.email && errors.email && <div>{errors.email}</div>} <Field type="password" name="password" /> {touched.password && errors.password && <div>{errors.password}</div>} <button type="submit" disabled={isSubmitting}> Submit </button> </Form> ); }; // The type of props MyForm receives interface MyFormProps { initialEmail?: string; message: string; // if this passed all the way through you might do this or make a union type } // Wrap our form with the using withFormik HoC const MyForm = withFormik<MyFormProps,FormValues>({ // Transform outer props into form values mapPropsToValues: props => { return { email: props.initialEmail || ‘‘,password: ‘‘,}; },// Add a custom validation function (this can be async too!) validate: (values: FormValues) => { let errors: FormikErrors = {}; if (!values.email) { errors.email = ‘Required‘; } else if (!isValidEmail(values.email)) { errors.email = ‘Invalid email address‘; } return errors; },handleSubmit: values => { // do submitting things },})(InnerForm); // Use <MyForm /> anywhere const Basic = () => ( <div> <h1>My App</h1> <p>This can be anywhere in your application</p> <MyForm message="Sign up" /> </div> ); export default Basic; How Form Submission WorksTo submit a form in Formik,you need to somehow fire off the provided handleSubmit(e) or submitForm prop. When you call either of these methods,Formik will execute the following (pseudo code) each time: "Pre-submit" Frequently Asked QuestionsHow do I determine if my submission handler is executing?Why does Formik touch all fields before submit?How do I protect against double submits?How do I know when my form is validating before submit?If isValidating is true and isSubmitting is true. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |