<ArrayInput>

To edit arrays of data embedded inside a record, <ArrayInput> creates a list of sub-forms.

Usage

<ArrayInput> allows editing of embedded arrays, like the items field in the following order record:

{
    "id": 1,
    "date": "2022-08-30",
    "customer": "John Doe",
    "items": [
        {
            "name": "Office Jeans",
            "price": 45.99,
            "quantity": 1,
        },
        {
            "name": "Black Elegance Jeans",
            "price": 69.99,
            "quantity": 2,
        },
        {
            "name": "Slim Fit Jeans",
            "price": 55.99,
            "quantity": 1,
        },
    ],
}

Tip: If you need to edit an array of strings, like a list of email addresses or a list of tags, you should use a <TextArrayInput> instead.

<ArrayInput> expects a single child, which must be a form iterator component. A form iterator is a component rendering a field array (the object returned by react-hook-form’s useFieldArray). For instance, the <SimpleFormIterator> component displays an array of react-admin Inputs in an unordered list (<ul>), one sub-form by list item (<li>). It also provides controls for adding and removing a sub-record.

import { 
    Edit,
    SimpleForm,
    TextInput,
    DateInput,
    ArrayInput,
    NumberInput,
    SimpleFormIterator
} from 'react-admin';

const OrderEdit = () => (
    <Edit>
        <SimpleForm>
            <TextInput source="customer" />
            <DateInput source="date" />
            <ArrayInput source="items">
                <SimpleFormIterator inline>
                    <TextInput source="name" helperText={false} />
                    <NumberInput source="price" helperText={false} />
                    <NumberInput source="quantity" helperText={false} />
                </SimpleFormIterator>
            </ArrayInput>
        </SimpleForm>
    </Edit>
);

Check the <SimpleFormIterator> documentation for details about how to customize the sub form layout.

Tip: If you need to edit an array of related records, i.e. if the items above actually come from another resource, you should use a <ReferenceManyInput> instead.

Note: Using shouldUnregister should be avoided when using <ArrayInput> (which internally uses useFieldArray) as the unregister function gets called after input unmount/remount and reorder. This limitation is mentioned in the react-hook-form documentation. If you are in such a situation, you can use the transform prop to manually clean the submitted values.

Props

<ArrayInput> accepts the common input props (except disabled, readOnly, format and parse).

Global validation

If you are using an <ArrayInput> inside a form with global validation, you need to shape the errors object returned by the validate function like an array too.

For instance, to display the following errors:

ArrayInput global validation

You need to return an errors object shaped like this:

        {
            authors: [
                {},
                {
                    name: 'A name is required', 
                    role: 'ra.validation.required' // translation keys are supported too
                },
            ],
        }

Tip: You can find a sample validate function that handles arrays in the Form Validation documentation.

Disabling The Input

<ArrayInput> does not support the disabled and readOnly props.

If you need to disable the input, set the <SimpleFormIterator disabled> prop, and make the child inputs readOnly:

const OrderEdit = () => (
    <Edit>
        <SimpleForm>
            <TextInput source="customer" />
            <DateInput source="date" />
            <ArrayInput source="items">
                <SimpleFormIterator inline disabled>
                    <TextInput source="name" readOnly/>
                    <NumberInput source="price" readOnly />
                    <NumberInput source="quantity" readOnly />
                </SimpleFormIterator>
            </ArrayInput>
        </SimpleForm>
    </Edit>
);

Changing An Item’s Value Programmatically

You can leverage react-hook-form’s setValue method to change an item’s value programmatically.

However you need to know the name under which the input was registered in the form, and this name is dynamically generated depending on the index of the item in the array.

To get the name of the input for a given index, you can leverage the SourceContext created by react-admin, which can be accessed using the useSourceContext hook.

This context provides a getSource function that returns the effective source for an input in the current context, which you can use as input name for setValue.

Here is an example where we leverage getSource and setValue to change the role of an user to ‘admin’ when the ‘Make Admin’ button is clicked:

import { ArrayInput, SimpleFormIterator, TextInput, useSourceContext } from 'react-admin';
import { useFormContext } from 'react-hook-form';
import { Button } from '@mui/material';

const MakeAdminButton = () => {
    const sourceContext = useSourceContext();
    const { setValue } = useFormContext();

    const onClick = () => {
        // sourceContext.getSource('role') will for instance return
        // 'users.0.role'
        setValue(sourceContext.getSource('role'), 'admin');
    };

    return (
        <Button onClick={onClick} size="small" sx={{ minWidth: 120 }}>
            Make admin
        </Button>
    );
};

const UserArray = () => (
    <ArrayInput source="users">
        <SimpleFormIterator inline>
            <TextInput source="name" helperText={false} />
            <TextInput source="role" helperText={false} />
            <MakeAdminButton />
        </SimpleFormIterator>
    </ArrayInput>
);

Tip: If you only need the item’s index, you can leverage the useSimpleFormIteratorItem hook instead.