Formik: How to correctly organize array of objects form?

Created on 13 Mar 2018  路  14Comments  路  Source: formium/formik

Bug, Feature, or Question?

Question

At the moment there is only one example of FieldArray which handles array of strings.
https://github.com/jaredpalmer/formik#fieldarray

Is it possible to implement an array of objects? In case each of friends has firstName, lastName and age for example.

What is a proper way to do it?

Most helpful comment

Here is what I used in my app:

<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`}/>
                        <button
                            type="button"
                            onClick={() => arrayHelpers.remove(index)}
                        >
                            -
                        </button>
                    </div>
                ))}
                <button
                    type="button"
                    onClick={() => arrayHelpers.push({ name: '', age: '' })}
                >
                    +
                </button>
            </div>
        )}
    />
</Form>

And here is an example validationSchema:

const friendValidationSchema = object().shape({
    name: string().required('name required'),
    age: number().required('age required').typeError('age must be a number')
});

const validationSchema = object().shape({
    friends: array().of(friendValidationSchema)
});

All 14 comments

Here is what I used in my app:

<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`}/>
                        <button
                            type="button"
                            onClick={() => arrayHelpers.remove(index)}
                        >
                            -
                        </button>
                    </div>
                ))}
                <button
                    type="button"
                    onClick={() => arrayHelpers.push({ name: '', age: '' })}
                >
                    +
                </button>
            </div>
        )}
    />
</Form>

And here is an example validationSchema:

const friendValidationSchema = object().shape({
    name: string().required('name required'),
    age: number().required('age required').typeError('age must be a number')
});

const validationSchema = object().shape({
    friends: array().of(friendValidationSchema)
});

@latviancoder thank you very much, it works!

@latviancoder Are you able to set initialValues using your approach? I'm currently using redux-form which doesn't seem to have support for initialValues + array of objects, so if Formik support it, I might as well move to Formik.

Yes you can, take a look at documentation :)

The documentation only has simple arrays but anyhow, I managed it with redux-forms. The trick was to not use FieldArray :smile:. Here's a CodeSandbox, if anyone comes looking for a similar solution.

@latviancoder how is it that friends.${index}.age should work as the input name when you pushed { name: '', age: '' } ?

If this is how things are supposed to work, then this is clearly not in in docs.

@jaredpalmer help?

You can use dots or brackets for nested things. friends.0.name is the same as friends[0].name

@jaredpalmer that's awesome, I think it'd be good to include the above code snippet into README. I don't mind doing a PR on this today if needs be. What do you think?

Yeah sounds good.馃憣

@jaredpalmer thanks for your help! 馃槃

@latviancoder @jaredpalmer Is it possible to combine the creation of a new element with an existing<FieldArray />?

In the same example @latviancoder posted, the button to add a new element into the array has this handler onClick={() => arrayHelpers.push({ name: '', age: '' })} from below:

<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`}/>
                        <button
                            type="button"
                            onClick={() => arrayHelpers.remove(index)}
                        >
                            -
                        </button>
                    </div>
                ))}
                <button
                    type="button"
                    onClick={() => arrayHelpers.push({ name: '', age: '' })}
                >
                    +
                </button>
            </div>
        )}
    />
</Form>

In this example this would just push { name: '', age: '' } element into the array of friends.

However, what if my form is a bit more complex, in that I want to render fields for the new element that I want to push into the array of this.props.values.friends.

The code that I imagine/hope for would be something like this (which I realize doesn't make much sense):

<Field name={`friends.${indexToBeAdded}.name`}/>
<Field name={`friends.${indexToBeAdded}.age`}/>

Currently what I have to do is create a separate set of fields for the new element..

<Field name={`new_friend_name`}/>
<Field name={`new_friend_age`}/>

Which means I have to have separate validation for those new fields new_friend_name and new_friend_age, and add a special handler that will add the new element into the existing this.props.values.friends using this.props.setValues upon successful validation.

Is there a more elegant way to accomplish this, where I can put the creation of a new element under the umbrella of the <FieldArray />?

Hopefully I'm illustrating the problem clearly enough. Chose not to create another issue as it seems closely tied to this, but certainly don't mind creating another one if you guys think so.

What about arrays with useFormik?
I have already used useFormik and now kinda confused of how to make arrays of components from initialValues, the values are updating but the component doesn't.
Please help

what if the array of objects is coming from a parent form using withFormik. We cannot take "values.friends.map" right.Can anybody help.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jaredpalmer picture jaredpalmer  路  3Comments

PeerHartmann picture PeerHartmann  路  3Comments

Jungwoo-An picture Jungwoo-An  路  3Comments

jaredpalmer picture jaredpalmer  路  3Comments

pmonty picture pmonty  路  3Comments