Form Array

Form arrays are a rather complicated topic, since you need to be able to dynamically add/remove forms on demand. react-fluent-form comes with a build in solution by providing two additional hooks: useFluentFormArray and useFluentFormItem. Keep following image in mind for examples below:

form-array-example

Creating array config

Like for single forms you also need to create a config for form arrays but using createFormArray function instead. It returns similar config as createForm but with additional configuration properties which are only relevant for form arrays:

  • withInitialArray: specifiy inital values for the form array
  • withKeyGenerator: items inside of the form array should be identifiable, which is why each form item has a unique key. On default the key will be generated by a key counter. To override this behaviour you can use this function to generate a key based on values.
caution

withKeyGenerator generates the key just once for each item directly when it's added.

const userRoleConfig = creatForm()({
username: field.text(),
role: field.select()
})
.withInitialArray([
{
id: 0,
username: "user0",
role: "admin"
},
{
id: 1,
username: "user1",
role: "manager"
}
])
.withKeyGenerator(item => item.id);

Decalaring form array

With the created array config you have all you need to declare and initialize the form array.

const UserRoleFormArray = () => {
const { formArray, addForm } = useFluentFormArray(arrayConfig);
return (
<form>
{formArray.map(item => (
<UserRoleForm key={item.key} formItem={item} />
))}
<button onClick={addForm}>Add User</button>
</form>
);
};

Declaring form item

Form items represent the actual forms inside the form array and can be created via useFluentFormItem hook. Since react hooks can not be called inside of loops (like map in the example above), a new component for form items needs to be implemented. useFluentFormItem returns the same properties as useFluentForm, but also following ones:

  • removeSelf: removes form item from the array
  • key: value, which is used to identify form item
const UserRoleForm = ({ formItem }) => {
const { removeSelf, handleSubmit /* and more.. */ } = useFluentFormItem(
formItem
);
return (
<div>
<label>
User:
<input {...fields.user} />
</label>
<label>
Role:
<select {...fields.role.select}>
<option {...fields.role.option("admin")}>Admin</option>
<option {...fields.role.option("manager")}>Manager</option>
</select>
</label>
<button onClick={removeSelf}>Remove</button>
</div>
);
};

Adding form items

useFluentFormArray returns a function - addForm - to add new form items. It optionally receives initialValues or a key key.

const { formArray, addForm } = useFluentFormArray(arrayConfig);
// will use initial values from config
// will use key generated by key counter or key generator if specified
addForm();
addForm({
initialValues: {
id: 2,
username: "user2",
role: "admin"
}
});
addForm({
key: 100
});

Remove form items

useFluentFormArray returns a function - removeForm - to remove form items, which requires a key as parameter.

const { formArray, removeForm } = useFluentFormArray(arrayConfig);
removeForm(0);
removeForm(100);

Reading form item state at top level

useFluentFormArray returns formStates, which is an array that stores the state of each form item. It can be accessed via index or via a helper function called getFormStateByKey.

info

Keys are generally not equal to the index!

const { formStates, getFormStateByKey } = useFluentFormArray(arrayConfig);
const firstFormItem = formState[0];
const formItemWithKeyHello = getFormStateByKey("hello");

Resetting array values

With resetArray the from array can be resetted. It will either reset to the array passed to withInitialArray or to the array set by setInitialArray.

const userRoleConfig = creatForm()({
username: field.text(),
role: field.select()
}).withInitialArray([
{
id: 0,
username: "user0",
role: "admin"
}
]);
const UserRoleFormArray = () => {
const { formStates, resetArray, setInitialArray } = useFluentFormArray(
userRoleConfig
);
const handleSave = () => {
setInitialArray(formStates.map(state => state.values));
};
return (
<form>
{formArray.map(item => (
<UserRoleForm key={item.key} formItem={item} />
))}
<button onClick={resetArray}>Reset Form</button>
<button onClick={handleSave}>Save Form</button>
</form>
);
};

Handling form array submission

Form array submission works just equal to single form submission.

const UserRoleFormArray = () => {
const { formStates, formArray, addForm, handleSubmit } = useFluentFormArray(
arrayConfig
);
const handleSubmitSuccess = () => {
console.log(formStates.map(state => state.values));
};
const handleSubmitFailure = () => {
console.log(formStates.map(state => state.errors));
};
return (
<form onSubmit={handleSubmit(handleSubmitSuccess, handleSubmitFailure)}>
{formArray.map(item => (
<UserRoleForm key={item.key} formItem={item} />
))}
<button onClick={addForm}>add user</button>
<button type="submit">Save</button>
</form>
);
};