Arrays and Lists
There can be situations in which the user needs to add or remove fields from a form depends on the amount of fields they need to fill out. Using React Cool Form, we can use the useFieldArray hook to easily deal with this situation.
#
Dealing with Array FieldsThe useFieldArray
hook helps you to deal with multiple similar fields. You pass it a name
parameter with the path of the field that holds the relevant array. The hook will then give you the power to render an array of inputs as well as common array/list manipulations.
note
import { useForm, useFieldArray } from "react-cool-form";
const TextField = ({ name, ...restProps }) => { const [fieldProps] = useControlled(name, restProps); return <input {...fieldProps} />;};
const App = () => { const { form } = useForm({ defaultValues: { foo: [{ name: "Iron Man" }, { name: "Hulk" }], }, onSubmit: (values) => console.log("onSubmit: ", values), }); const [fields, { push, insert, move, swap, remove }] = useFieldArray("foo");
return ( <form ref={form}> {/* The first parameter of the callback supplies you a field name (e.g. foo[0], foo[1]) */} {fields.map((fieldName, index) => ( // Use the "fieldName" as the key <div key={fieldName}> {/* Use the "fieldName" + "YOUR PATH" as the name */} <input name={`${fieldName}.name`} /> {/* Working with a controlled component */} <TextField name={`${fieldName}.name`} /> <button type="button" onClick={() => remove(index)}> โ </button> </div> ))} <button type="button" onClick={() => push({ name: "Thor" })}> โ </button> <input type="submit" /> </form> );};
#
Applying FocusWhen dealing with dynamic list fields we can apply focus to a new field by the focus method to provide better for the user.
tip
When working with nested fields, we can just pass in the parent path to the focus
method. It will apply the focus to the first field.
import { useForm, useFieldArray } from "react-cool-form";
const App = () => { const { form, focus } = useForm({ defaultValues: { foo: [{ name: "Iron Man", quote: "I'm Iron Man" }], }, onSubmit: (values) => console.log("onSubmit: ", values), }); const [fields, { push, remove }] = useFieldArray("foo");
return ( <form ref={form}> {fields.map((fieldName, index) => ( <div key={fieldName}> <input name={`${fieldName}.name`} /> <input name={`${fieldName}.quote`} /> <button type="button" onClick={() => remove(index)}> โ </button> </div> ))} <button type="button" onClick={() => { push({ name: "Thor", quote: "I Knew It" }); // We need to wait for the item rendered (delay = 0 is acceptable) then apply focus to the first field focus(`foo[${fields.length}]`, 300); // You can also apply focus to a specified field focus(`foo[${fields.length}].quote`, 300); }} > โ </button> <input type="submit" /> </form> );};
#
Conditional FieldsThe useFieldArray
hook also supports you to work with conditional fields.
import { useState } from "react";import { useForm, useFieldArray } from "react-cool-form";
const TextField = ({ name, ...restProps }) => { const [fieldProps] = useControlled(name, restProps); return <input {...fieldProps} />;};
const App = () => { const [toggle, setToggle] = useState(false); const { form } = useForm({ defaultValues: { foo: [{ name: "Iron Man" }] }, onSubmit: (values) => console.log("onSubmit: ", values), }); const [fields] = useFieldArray("foo");
return ( <form ref={form}> {fields.map((fieldName) => ( <div key={fieldName}> <input name={`${fieldName}.name`} /> {toggle && ( <input name={`${fieldName}.quote`} defaultValue="I'm Iron Man" // Provide the default value /> )} <TextField name={`${fieldName}.name`} /> {toggle && ( <TextField name={`${fieldName}.quote`} defaultValue="I'm Iron Man" // Provide the default value /> )} </div> ))} <button type="button" onClick={() => setToggle(!toggle)}> Toggle </button> <input type="submit" /> </form> );};
#
Top-level Field ValidationYou can validate the top-level field via the Form-level Validation or Field-level Validation (via the validate
option), depending on your case. React Cool Form runs validation after any array manipulations.
import { useForm, useFieldArray } from "react-cool-form";
const App = () => { const { form } = useForm({ // Form-level validation validate: ({ foo }) => { const errors = {};
if (!foo.length) errors.foo = "We need a super hero!";
return errors; }, onSubmit: (values) => console.log("onSubmit: ", values), onError: (errors) => console.log("onError: ", errors), }); const [fields, { push, remove }] = useFieldArray("foo", { // Field-level validation validate: (value, values /* Form values */) => !foo.length ? "We need a super hero!" : false, });
return ( <form ref={form} noValidate> {fields.map((fieldName, index) => ( <div key={fieldName}> <input name={`${fieldName}.name`} /> <button type="button" onClick={() => remove(index)}> โ </button> </div> ))} <button type="button" onClick={() => push({ name: "Thor" })}> โ </button> <input type="submit" /> </form> );};