DEV Community

André Ovalle
André Ovalle

Posted on

Integrating Nested Objects with Formik Hooks in TypeScript: A Comprehensive Guide

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:  "",
    },
};
Enter fullscreen mode Exit fullscreen mode

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"),
    }),
});
Enter fullscreen mode Exit fullscreen mode

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();
    },
});
Enter fullscreen mode Exit fullscreen mode

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)  })}
/>
Enter fullscreen mode Exit fullscreen mode

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>
    );
}
Enter fullscreen mode Exit fullscreen mode

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)