<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, null, 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: string
#
The name or path to the relevant key in values
.
validateOnChange?: boolean
#
Default 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}> {/** both these conventions do the same */} <Field name={`friends[${index}].name`} /> <Field name={`friends.${index}.age`} />
<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 constraints)...
Bad
// within a `FieldArray`'s renderconst FriendArrayErrors = errors => errors.friends ? <div>{errors.friends}</div> : null; // app will crash
Good
// within a `FieldArray`'s renderconst 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.
push: (obj: any) => void
: Add a value to the end of an arrayswap: (indexA: number, indexB: number) => void
: Swap two values in an arraymove: (from: number, to: number) => void
: Move an element in an array to another indexinsert: (index: number, value: any) => void
: Insert an element at a given index into the arrayunshift: (value: any) => number
: Add an element to the beginning of an array and return its lengthremove<T>(index: number): T | undefined
: Remove an element at an index of an array and return itpop<T>(): T | undefined
: Remove and return value from the end of the arrayreplace: (index: number, value: any) => void
: Replace a value at the given index into the array
#
FieldArray render methodsThere are three ways to render things with <FieldArray />
<FieldArray name="..." component>
<FieldArray name="..." render>
<FieldArray name="..." children>
render: (arrayHelpers: ArrayHelpers) => React.ReactNode
#
import React from 'react';import { Formik, Form, Field, FieldArray } from 'formik'
export const FriendList = () => ( <div> <h1>Friend List</h1> <Formik initialValues={{ friends: ['jared', 'ian', 'brent'] }} onSubmit={...} render={formikProps => ( <FieldArray name="friends" render={({ move, swap, push, insert, unshift, pop }) => ( <Form> {/*... use these however you want */} </Form> )} /> /> </div>);
component: React.ReactNode
#
import React from 'react';import { Formik, Form, Field, FieldArray } from 'formik'
export const FriendList = () => ( <div> <h1>Friend List</h1> <Formik initialValues={{ friends: ['jared', 'ian', '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`// propexport const MyDynamicForm = ({ move, swap, push, insert, unshift, pop, form}) => ( <Form> {/** whatever you need to do */} </Form>);
children: func
#
import React from 'react';import { Formik, Form, Field, FieldArray } from 'formik'
export const FriendList = () => ( <div> <h1>Friend List</h1> <Formik initialValues={{ friends: ['jared', 'ian', 'brent'] }} onSubmit={...} render={formikProps => ( <FieldArray name="friends"> {({ move, swap, push, insert, unshift, pop, form }) => { return ( <Form> {/*... use these however you want */} </Form> ); }} </FieldArray> )} /> </div>);