使用Formik轻松开发更高质量的React表单(四)其他几个API解析
(下面部分内容正处于翻译中,敬请稍等......)
<Field /><Field /> will automagically hook up inputs to Formik. It uses the name attribute to match up with Formik state. <Field /> will default to an <input /> element. To change the underlying element of <Field />,specify a component prop. It can either be a string like select or another React component. <Field /> can also take a render prop. import React from ‘react‘; import { Formik,Field } from ‘formik‘; const Example = () => ( <div> <h1>My Form</h1> <Formik initialValues={{ email: ‘‘,color: ‘red‘,firstName: ‘‘ }} onSubmit={(values,actions) => { setTimeout(() => { alert(JSON.stringify(values,null,2)); actions.setSubmitting(false); },1000); }} render={(props: FormikProps<Values>) => ( <form onSubmit={props.handleSubmit}> <Field type="email" name="email" placeholder="Email" /> <Field component="select" name="color"> <option value="red">Red</option> <option value="green">Green</option> <option value="blue">Blue</option> </Field> <Field name="firstName" component={CustomInputComponent} /> <Field name="lastName" render={({ field /* _form */ }) => ( <input {...field} placeholder="firstName" /> )} /> <button type="submit">Submit</button> </form> )} /> </div> ); const CustomInputComponent: React.SFC< FieldProps<Values> & CustomInputProps > = ({ field,// { name,value,onChange,onBlur } form: { touched,errors },// also values,setXXXX,handleXXXX,dirty,isValid,status,etc. ...props }) => ( <div> <input type="text" {...field} {...props} /> {touched[field.name] && errors[field.name] && <div className="error">{errors[field.name]}</div>} </div> ); validate?: (value: any) => undefined | string | Promise<any>You can run independent field-level validations by passing a function to the validate prop. The function will respect the validateOnBlur and validateOnChange config/props specified in the <Field>‘s parent <Formik> / withFormik. This function can be either be: Synchronous and if invalid,return a string containing the error message or return undefined. // Synchronous validation for Field const validate = value => { let errorMessage; if (!/^[A-Z0-9._%+-][email?protected][A-Z0-9.-]+.[A-Z]{2,4}$/i.test(value)) { errorMessage = ‘Invalid email address‘; } return errorMessage; }; async: Return a Promise that throws a string containing the error message. This works like Formik‘s validate,but instead of returning an errors object,it‘s just a string. Asynchronous and return a Promise that‘s error is an string with the error message // Async validation for Field const sleep = ms => new Promise(resolve => setTimeout(resolve,ms)); const validate = value => { return sleep(2000).then(() => { if ([‘admin‘,‘null‘,‘god‘].includes(value)) { throw ‘Nice try‘; } }); }; Note: To allow for i18n libraries,the TypeScript typings for validate are slightly relaxed and allow you to return a Function (e.g. i18n(‘invalid‘)). RefsWhen you are not using a custom component and you need to access the underlying DOM node created by Field (e.g. to call focus),pass the callback to the innerRef prop instead. <FieldArray /><FieldArray /> is a component that helps with common array/list manipulations. You pass it a name property with the path to the key within values that holds the relevant array. <FieldArray /> will then give you access to array helper methods via render props. For convenience,calling these methods will trigger validation and also manage touched for you. import React from ‘react‘; import { Formik,Form,Field,FieldArray } from ‘formik‘; // Here is an example of a form with an editable list. // Next to each input are buttons for insert and remove. // If the list is empty,there is a button to add an item. export const FriendList = () => ( <div> <h1>Friend List</h1> <Formik initialValues={{ friends: [‘jared‘,‘ian‘,‘brent‘] }} onSubmit={values => setTimeout(() => { alert(JSON.stringify(values,2)); },500) } render={({ values }) => ( <Form> <FieldArray name="friends" render={arrayHelpers => ( <div> {values.friends && values.friends.length > 0 ? ( values.friends.map((friend,index) => ( <div key={index}> <Field name={`friends.${index}`} /> <button type="button" onClick={() => arrayHelpers.remove(index)} // remove a friend from the list > - </button> <button type="button" onClick={() => arrayHelpers.insert(index,‘‘)} // insert an empty string at a position > + </button> </div> )) ) : ( <button type="button" onClick={() => arrayHelpers.push(‘‘)}> {/* show this when user has removed all friends from the list */} Add a friend </button> )} <div> <button type="submit">Submit</button> </div> </div> )} /> </Form> )} /> </div> ); name: stringThe name or path to the relevant key in values. validateOnChange?: booleanDefault is true. Determines if form validation should or should not be run after any array manipulations. FieldArray Array of ObjectsYou can also iterate through an array of objects,by following a convention of object[index]property or object.index.property for the name attributes of <Field /> or <input /> elements in <FieldArray />. <Form> <FieldArray name="friends" render={arrayHelpers => ( <div> {values.friends.map((friend,index) => ( <div key={index}> <Field name={`friends[${index}]name`} /> <Field name={`friends.${index}.age`} /> // both these conventions do the same <button type="button" onClick={() => arrayHelpers.remove(index)}> - </button> </div> ))} <button type="button" onClick={() => arrayHelpers.push({ name: ‘‘,age: ‘‘ })} > + </button> </div> )} /> </Form> FieldArray Validation GotchasValidation can be tricky with <FieldArray>. If you use validationSchema and your form has array validation requirements (like a min length) as well as nested array field requirements,displaying errors can be tricky. Formik/Yup will show validation errors inside out. For example, const schema = Yup.object().shape({ friends: Yup.array() .of( Yup.object().shape({ name: Yup.string() .min(4,‘too short‘) .required(‘Required‘),// these constraints take precedence salary: Yup.string() .min(3,‘cmon‘) .required(‘Required‘),// these constraints take precedence }) ) .required(‘Must have friends‘) // these constraints are shown if and only if inner constraints are satisfied .min(3,‘Minimum of 3 friends‘),}); Since Yup and your custom validation function should always output error messages as strings,you‘ll need to sniff whether your nested error is an array or a string when you go to display it. So...to display ‘Must have friends‘ and ‘Minimum of 3 friends‘ (our example‘s array validation contstraints)... Bad// within a `FieldArray`‘s render const FriendArrayErrors = errors => errors.friends ? <div>{errors.friends}</div> : null; // app will crash Good// within a const FriendArrayErrors = errors => typeof errors.friends === ‘string‘ ? <div>{errors.friends}</div> : null; For the nested field errors,you should assume that no part of the object is defined unless you‘ve checked for it. Thus,you may want to do yourself a favor and make a custom <ErrorMessage /> component that looks like this: import { Field,getIn } from ‘formik‘; const ErrorMessage = ({ name }) => ( <Field name={name} render={({ form }) => { const error = getIn(form.errors,name); const touch = getIn(form.touched,name); return touch && error ? error : null; }} /> ); // Usage <ErrorMessage name="friends[0].name" />; // => null,‘too short‘,or ‘required‘ NOTE: In Formik v0.12 / 1.0,a new meta prop may be added to Field and FieldArray that will give you relevant metadata such as error & touch,which will save you from having to use Formik or lodash‘s getIn or checking if the path is defined on your own. FieldArray HelpersThe following methods are made available via render props.
FieldArray render methodsThere are three ways to render things with <FieldArray /> <FieldArray name="..." component> <FieldArray name="..." render> render: (arrayHelpers: ArrayHelpers) => React.ReactNode import React from ‘react‘; import { Formik,FieldArray } from ‘formik‘ export const FriendList = () => ( <div> <h1>Friend List</h1> <Formik initialValues={{ friends: [‘jared‘,‘brent‘] }} onSubmit={...} render={formikProps => ( <FieldArray name="friends" render={({ move,swap,push,insert,unshift,pop }) => ( <Form> {/*... use these however you want */} </Form> )} /> /> </div> ); component: React.ReactNodeimport React from ‘react‘; import { Formik,‘brent‘] }} onSubmit={...} render={formikProps => ( <FieldArray name="friends" component={MyDynamicForm} /> /> </div> ); // In addition to the array helpers,Formik state and helpers // (values,touched,setXXX,etc) are provided through a `form` // prop export const MyDynamicForm = ({ move,pop,form }) => ( <Form> {/** whatever you need to do */} </Form> ); <Form />Like <Field />,<Form /> is a helper component you can use to save time. It is tiny wrapper around <form onSubmit={context.formik.handleSubmit} />. This means you don‘t need to explictly type out <form onSubmit={props.handleSubmit} /> if you don‘t want to. ReactDOM onlyimport React from ‘react‘; import { Formik,Form } from ‘formik‘; const Example = () => ( <div> <h1>My Form</h1> <Formik initialValues={{ email: ‘‘,color: ‘red‘ }} onSubmit={(values,1000); }} component={MyForm} /> </div> ); const MyForm = () => ( <Form> <Field type="email" name="email" placeholder="Email" /> <Field component="select" name="color"> <option value="red">Red</option> <option value="green">Green</option> <option value="blue">Blue</option> </Field> <button type="submit">Submit</button> </Form> ); withFormik(options)Create a higher-order React component class that passes props and form handlers (the "FormikBag") into your component derived from supplied options. optionsdisplayName?: stringWhen your inner form component is a stateless functional component,you can use the displayName option to give the component a proper name so you can more easily find it in React DevTools. If specified,your wrapped form will show up as Formik(displayName). If omitted,it will show up as Formik(Component). This option is not required for class components (e.g. class XXXXX extends React.Component {..}). enableReinitialize?: booleanDefault is false. Control whether Formik should reset the form if the wrapped component props change (using deep equality). handleSubmit: (values: Values,formikBag: FormikBag) => voidYour form submission handler. It is passed your forms values and the "FormikBag",which includes an object containing a subset of the injected props and methods (i.e. all the methods with names that start with set<Thing> + resetForm) and any props that were passed to the the wrapped component. The "FormikBag": isInitialValid?: boolean | (props: Props) => booleanDefault is false. Control the initial value of isValid prop prior to mount. You can also pass a function. Useful for situations when you want to enable/disable a submit and reset buttons on initial mount. mapPropsToValues?: (props: Props) => ValuesIf this option is specified,then Formik will transfer its results into updatable form state and make these values available to the new component as props.values. If mapPropsToValues is not specified,then Formik will map all props that are not functions to the inner component‘s props.values. That is,if you omit it,Formik will only pass props where typeof props[k] !== ‘function‘,where k is some key. Even if your form is not receiving any props from its parent,use mapPropsToValues to initialize your forms empty state. validate?: (values: Values,props: Props) => FormikErrors<Values> | Promise<any>Note: I suggest using validationSchema and Yup for validation. However,validate is a dependency-free,straightforward way to validate your forms. Validate the form‘s values with function. This function can either be: Synchronous and return an errors object. // Synchronous validation const validate = (values,props) => { let errors = {}; if (!values.email) { errors.email = ‘Required‘; } else if (!/^[A-Z0-9._%+-][email?protected][A-Z0-9.-]+.[A-Z]{2,4}$/i.test(values.email)) { errors.email = ‘Invalid email address‘; } //... return errors; }; Asynchronous and return a Promise that‘s error is an errors object // Async Validation const sleep = ms => new Promise(resolve => setTimeout(resolve,ms)); const validate = (values,props) => { return sleep(2000).then(() => { let errors = {}; if ([‘admin‘,‘god‘].includes(values.username)) { errors.username = ‘Nice try‘; } // ... if (Object.keys(errors).length) { throw errors; } }); }; validateOnBlur?: booleanDefault is true. Use this option to run validations on blur events. More specifically,when either handleBlur,setFieldTouched,or setTouched are called. validateOnChange?: booleanDefault is true. Use this option to tell Formik to run validations on change events and change-related methods. More specifically,when either handleChange,setFieldValue,or setValues are called. validationSchema?: Schema | ((props: Props) => Schema)A Yup schema or a function that returns a Yup schema. This is used for validation. Errors are mapped by key to the inner component‘s errors. Its keys should match those of values. Injected props and methodsThese are identical to the props of <Formik render={props => ...} /> 在Formik中使用connect()connect()是一个高阶组件,that injects raw Formik context as prop called formik into the inner component. Fun fact: Formik uses connect() under the hood to wire up <Field/>,<FastField>,and <Form>. Advanced users may find it useful to use connect() when building custom components. import { connect } from ‘formik‘; const SubmitCount = ({ formik }) => <div>{formik.submitCount}</div>; export default connect(SubmitCount); (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |