Categories
React

Make Form Handling Easy in React Apps with Formik — Dependant Fields and Object Array Fields

Spread the love

Formik is a library that makes form handling in React apps easy.

In this article, we’ll look at how to handle form inputs with Formik

Dependent Fields

We can set one field’s value based on a value of another field.

For instance, we can write:

import React from "react";
import { Formik, Field, Form, useField, useFormikContext } from "formik";

const MyField = (props) => {
  const {
    values: { textA },
    touched,
    setFieldValue
  } = useFormikContext();
  const [field, meta] = useField(props);

  React.useEffect(() => {
    if (textA.trim() !== "" && touched.textA) {
      setFieldValue(props.name, `textA: ${textA}`);
    }
  }, [textA, touched.textA, setFieldValue, props.name]);

  return (
    <>
      <input {...props} {...field} />
      {!!meta.touched && !!meta.error && <div>{meta.error}</div>}
    </>
  );
};

const initialValues = { textA: "", textB: "" };

export const FormExample = () => (
  <div>
    <Formik
      initialValues={initialValues}
      onSubmit={async (values) => alert(JSON.stringify(values, null, 2))}
    >
      <Form>
        <label>
          textA
          <Field name="textA" />
        </label>
        <label>
          textB
          <MyField name="textB" />
        </label>
        <button type="submit">Submit</button>
      </Form>
    </Formik>
  </div>
);
export default function App() {
  return (
    <div className="App">
      <FormExample />
    </div>
  );
}

We created the MyField component and use the useFormikContext component to get the status of the textA field.

We get its value from the values.textA property.

touched has the touched value.

setFieldValue lets us set the field value of another field.

We watch the value the textA field with the useEffect callback and the array with the textA field value, the touched.textA property which has the touched status of textA , setFieldValue , and props.name .

We watch all of them for changes.

Then in FormExample , we use MyField to create the textB field.

It watches the textA field for changes, so when we enter something into the textA field, we see the text of the textB set with the value of textA .

Object Array Fields

We can bind fields to an array of objects.

To do this, we write:

import React from "react";
import { Formik, Field, Form, FieldArray, ErrorMessage } from "formik";

const initialValues = {
  friends: [
    {
      name: "",
      email: ""
    }
  ]
};

export const FormExample = () => (
  <div>
    <Formik
      initialValues={initialValues}
      onSubmit={async (values) => {
        await new Promise((r) => setTimeout(r, 500));
        alert(JSON.stringify(values, null, 2));
      }}
    >
      {({ values }) => (
        <Form>
          <FieldArray name="friends">
            {({ remove, push }) => (
              <div>
                {values.friends.length > 0 &&
                  values.friends.map((friend, index) => (
                    <div className="row" key={index}>
                      <div className="col">
                        <label htmlFor={`friends.${index}.name`}>Name</label>
                        <Field name={`friends.${index}.name`} type="text" />
                        <ErrorMessage
                          name={`friends.${index}.name`}
                          component="div"
                          className="field-error"
                        />
                      </div>
                      <div className="col">
                        <label htmlFor={`friends.${index}.email`}>Email</label>
                        <Field name={`friends.${index}.email`} type="email" />
                        <ErrorMessage
                          name={`friends.${index}.name`}
                          component="div"
                          className="field-error"
                        />
                      </div>
                      <div className="col">
                        <button
                          type="button"
                          className="secondary"
                          onClick={() => remove(index)}
                        >
                          X
                        </button>
                      </div>
                    </div>
                  ))}
                <button
                  type="button"
                  className="secondary"
                  onClick={() => push({ name: "", email: "" })}
                >
                  Add
                </button>
              </div>
            )}
          </FieldArray>
          <button type="submit">Invite</button>
        </Form>
      )}
    </Formik>
  </div>
);

export default function App() {
  return (
    <div className="App">
      <FormExample />
    </div>
  );
}

We just set the name to the path of the property we want to bind to in the Field component.

Likewise, we do the same with the ErrorMessage field to get the form validation error messages for the field with the given property path.

To remove an entry, we can use the remove function that comes with Formik.

Conclusion

We can add dependant fields and bind fields to an array of objects with Formik.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *