1// Render Prop 2import React from 'react'; 3import { Formik, FormikErrors } from 'formik'; 4 5type MyData = {email: string, password: string}; 6declare function LoginToMyApp(data: MyData): Promise<{user: string}>; 7declare function transformMyApiErrors(o: any): FormikErrors<never>; 8 9const Basic = () => ( 10 <div> 11 <h1>My Form</h1> 12 <p>This can be anywhere in your application</p> 13 {/* 14 The benefit of the render prop approach is that you have full access to React's 15 state, props, and composition model. Thus there is no need to map outer props 16 to values...you can just set the initial values, and if they depend on props / state 17 then--boom--you can directly access to props / state. 18 19 The render prop accepts your inner form component, which you can define separately or inline 20 totally up to you: 21 - `<Formik render={props => <form>...</form>}>` 22 - `<Formik component={InnerForm}>` 23 - `<Formik>{props => <form>...</form>}</Formik>` (identical to as render, just written differently) 24 */} 25 <Formik 26 initialValues={{ 27 email: '', 28 password: '', 29 }} 30 validate={values => { 31 // same as above, but feel free to move this into a class method now. 32 let errors: FormikErrors<MyData> = {} as FormikErrors<MyData>; // FormikErrors<MyData> isn't optionalized, so in strict null checks this needs a cast 33 if (!values.email) { 34 errors.email = 'Required'; 35 } else if ( 36 !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email) 37 ) { 38 errors.email = 'Invalid email address'; 39 } 40 return errors; 41 }} 42 onSubmit={( 43 values, 44 { setSubmitting, setErrors /* setValues and other goodies */ } 45 ) => { 46 LoginToMyApp(values).then( 47 user => { 48 setSubmitting(false); 49 // do whatevs... 50 // props.updateUser(user) 51 }, 52 errors => { 53 setSubmitting(false); 54 // Maybe transform your API's errors into the same shape as Formik's 55 setErrors(transformMyApiErrors(errors)); 56 } 57 ); 58 }} 59 render={({ 60 values, 61 errors, 62 touched, 63 handleChange, 64 handleBlur, 65 handleSubmit, 66 isSubmitting, 67 }) => ( 68 <form onSubmit={handleSubmit}> 69 <input 70 type="email" 71 name="email" 72 onChange={handleChange} 73 onBlur={handleBlur} 74 value={values.email} 75 /> 76 {touched.email && errors.email && <div>{errors.email}</div>} 77 <input 78 type="password" 79 name="password" 80 onChange={handleChange} 81 onBlur={handleBlur} 82 value={values.password} 83 /> 84 {touched.password && errors.password && <div>{errors.password}</div>} 85 <button type="submit" disabled={isSubmitting}> 86 Submit 87 </button> 88 </form> 89 )} 90 /> 91 </div> 92); 93 94export default Basic;