When embarking on a project that involves using a library, it’s common practice to consult the library’s documentation to understand its usage. One such library is Formik. This powerful tool not only handles form validations and submit events but also provides integrated inputs with Formik and hooks for those who prefer not to use the provided inputs.
Despite the comprehensive examples provided in the Formik documentation, one area that lacks detailed examples is the use of hooks with nested objects. This article aims to fill that gap and provide guidance on integrating nested objects using only the library hooks.
For those interested in a hands-on approach, all the code used for this tutorial can be found here.
Additionally, a working demo is available here.
Getting Started with Initial Values
In this example, we will use the useFormik hook. However, the techniques discussed can be applied to any hook in the library. We begin by declaring the initial object along with its typing and validations by Yup library.
interface UserInfo {
personal: {
name: string;
lastName: string;
age: number;
};
location: {
address: string;
country: string;
};
}
const initialValues: UserInfo = {
personal: {
name: "",
lastName: "",
age: 0,
},
location: {
address: "",
country: "",
},
};
We also inititialize validators that we for our form. For this example we will use validators from the Yup library to simplify our validations.
const validationSchema = yup.object().shape({
personal: yup.object().shape({
name: yup.string().required("Name is required"),
lastName: yup.string().required("Last name is required"),
age: yup
.number()
.required("Age is required")
.min(1, "You should have at least 1 year"),
}),
location: yup.object().shape({
address: yup.string().required("Address is required"),
country: yup.string().required("Country is required"),
}),
});
Formik Initialization
Next, we initialize Formik, which will be used to manage our form.
const formik = useFormik<UserInfo>({
initialValues,
validationSchema,
onSubmit: (values, { resetForm }) => {
console.log("values: ", values);
toast.current?.show({
severity: "success",
summary: "Form submitted",
detail: "The form was submitted",
});
resetForm();
},
});
Next we will explain the inputs and key to use it with nested objects.
Inputs: The Key to Using Nested Objects
To avoid redundancy, we will create some specialized inputs for use in our example form. Here, we expose one of the three types of input that we will create for this form. The other two can be found in the demo or in the code repository.
<InputText
id={name}
name={name}
value={get(formik.values, name)}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
className={classNames({ "p-invalid": isInvalid(name, formik) })}
/>
For this type of input we can detect changes in the same way as normal fields. To get the value you need to access the nested object and then the specific field. Formik uses the following syntax: "ob1.ob2...objn.field" to manage all updates to nested objects. You can use a helper function to access to the information that uses the syntax described above. In this example we will use a get function from the Lodash library to get the value.
The Result
After examining the components that make up the form, we arrive at something similar to the following:
function App() {
const toast = useRef<Toast>(null);
const formik = useFormik<UserInfo>({
initialValues,
validationSchema,
onSubmit: (values, { resetForm }) => {
console.log("values: ", values);
toast.current?.show({
severity: "success",
summary: "Form submitted",
detail: "The form was submitted",
});
resetForm();
},
});
return (
<div className="App">
<Toast ref={toast} />
<form
className="w-full h-full flex flex-column align-items-center"
onSubmit={formik.handleSubmit}
>
<h1>useFormik with nested objects</h1>
<h2>Personal Info</h2>
<div className="flex flex-column gap-4">
<MInputText
name="personal.name"
label="Name"
formik={formik}
/>
<MInputText
name="personal.lastName"
label="Last name"
formik={formik}
/>
<MInputNumber
name="personal.age"
label="Age"
formik={formik}
/>
</div>
<h2>Address Info</h2>
<div className="flex flex-column gap-4">
<MInputText
name="location.address"
label="Address"
formik={formik}
/>
<MDropdown
name="location.country"
label="Country"
formik={formik}
options={countries}
/>
</div>
<Button
label="Send Form"
type="submit"
className="mt-4"
/>
</form>
</div>
);
}
Using nested objects with useFormik hooks is not as daunting as it may seem. The key is to remember to use the obj.field notation to edit the fields and an auxiliary function to obtain the values.
Bibliography
Formik: https://formik.org/docs/overview
Top comments (0)