ReactHustle

Tutorial: How to Reset Formik Form

Jasser Mark Arioste

Jasser Mark Arioste

Tutorial: How to Reset Formik Form

In one of my projects, I recently had to implement a functionality where in the backend/cms where the save button will be disabled after submitting the changes. This can be done by using the really awesome react library called Formik.

What is Formik? #

Formik is a relatively small react library used for managing forms in react. It allows us to build forms  and manage form state without the headache. This is especially useful if you're building a huge form like for example in the backend Edit user, Edit post, etc. A post or a user has many properties just managing the errors or state of the page is borderline impossible without something like Formik. 

One thing I like about Formik is it's simplicity when integrating with UI libraries like MUI.

How to reset the form and disable the save button? #

One way to disable the save button is to check if the form is dirty using dirty flag from formik.  dirty will be set to true when there's any change in the initial state of the form. The way to modify dirty is by calling resetForm helper function provided by Formik.

In this tutorial I'll be discussing the following scenarios:

  1. resetting the form inside onSubmit function
  2. Resetting the Form when using <Formik> provider
  3. Resetting the Form when using isFormik hook

Scenario 1: Resetting the form inside onSubmit function #

In simple scenarios, we can just reset the form inside onSubmit. For example let's see the form below:


//EditUserForm.tsx
import { Field, Form, Formik } from "formik";
import React from "react";
import SaveButton from "./SaveButton";

const EditUserForm = () => {
  //just a mock save function, let's pretend this calls the server
  const save = async () => {
    return {
      error: Math.random() > 0.5 ? "failed to save" : "",
    };
  };
  return (
    <Formik
      onSubmit={async (values, helpers) => {
        const { error } = await save();

        // reset the form if it is saved successfully
        //now the initial state will be the values submitted.
        if (!error) {
          helpers.resetForm({
            values,
          });
        }
      }}
      initialValues={{ name: "John Doe", email: "johndoe@gmail.com" }}
    >
      <Form className="flex flex-col gap-2 max-w-lg bg-base-200 shadow-md p-10">
        <h1 className="text-xl"> Edit User</h1>

        <Field
          name="name"
          type="text"
          placeholder="name"
          className="input input-bordered"
        />
        <Field
          name="email"
          type="email"
          placeholder="email"
          className="input input-bordered"
        />
        {/* custom save button  */}
        <SaveButton />
      </Form>
    </Formik>
  );
};

export default EditUserForm;
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051

//SaveButton.tsx
import { useFormikContext } from "formik";
import React from "react";

const SaveButton = () => {
  //access formik context to check if the form is dirty.
  const { dirty } = useFormikContext();
  return (
    <>
      <button type="submit" className="btn btn-primary" disabled={!dirty}>
        Save
      </button>
    </>
  );
};

export default SaveButton;
123456789101112131415161718

In the codes above we have two components, EditUserForm and SaveButton. In this specific use case, The submit button has to be disabled when there are no changes to the fields and when the save is successful. It only becomes enabled when the user edits the fields.

Let's see it in action.

Notice the save button. Once we click save the form is reset but we're using the updated values as the initial values. We achieved this by passing an { values } option to the resetForm method. If we want to reset the form using the original values, we don't have to pass anything to resetForm.

Formik how to reset form and update the values

With this I've solved my use case.

Now let's talk about other use cases. Let's say we don't want to reset the form inside onSubmit and just have a reset button inside the form so that the user can go back to the initial values. There are two ways to use formik and let's discuss how to achieve this in both ways:

Scenario 2: Resetting the form when using <Formik> provider. #

Almost 100% of the time, I use <Formik> provider when creating forms because it has a clear boundary of the scope/context of form. You can have nested <Formik> provider components and there will be no problem.

So in order to access resetForm helper, there are two ways:

  1. Using renderProps
  2. Creating a new component and using useFormikContext

Using renderProps to access Formik Context

Let's modify our form component and add a reset button beside the save button. The reset button should only be enabled if the form is dirty and when click it resets the form to it's original state.

//EditUserForm.tsx
import { Field, Form, Formik } from "formik";
import React from "react";
import SaveButton from "./SaveButton";

const EditUserForm = () => {
  ...
  return (
    <Formik >
      ...
      {/* we can use render props to get a hold of the formik context */}
      {({ dirty, resetForm }) => {
        return (
          <Form className="flex flex-col gap-2 max-w-lg bg-base-200 shadow-md p-10">
            <h1 className="text-xl"> Edit User</h1>

            <Field
              name="name"
              type="text"
              placeholder="name"
              className="input input-bordered"
            />
            <Field
              name="email"
              type="email"
              placeholder="email"
              className="input input-bordered"
            />
            <div className="flex gap-2 ">
              <button
                className="btn btn-outline btn-error"
                disabled={!dirty}
                onClick={() => resetForm()}
              >
                Reset
              </button>
              <SaveButton />
            </div>
          </Form>
        );
      }}
    </Formik>
  );
};

export default EditUserForm;
12345678910111213141516171819202122232425262728293031323334353637383940414243444546

This already gives us the functionality we need but I prefer refactoring it using next method since it's a bit cleaner than using renderProps.

Creating a new component and using useFormikContext

Let's create a new component called ResetButton.tsx and use useFormikContext hook to access the form state and helper functions from inside the component.

//ResetButton.tsx
import { useFormikContext } from "formik";
import React from "react";

const ResetButton = () => {
  //access formik context to check if the form is dirty.
  const { dirty, resetForm } = useFormikContext();
  return (
    <>
      <button
        type="submit"
        className="btn btn-primary"
        disabled={!dirty}
        onClick={() => resetForm()}
      >
        Reset
      </button>
    </>
  );
};

export default ResetButton;
12345678910111213141516171819202122

Usage inside EditUserForm.tsx

//EditUserForm.tsx
import { Field, Form, Formik } from "formik";
import React from "react";
import ResetButton from "./ResetButton";
import SaveButton from "./SaveButton";

const EditUserForm = () => {
  ...
  return (
    <Formik>
      <Form className="flex flex-col gap-2 max-w-lg bg-base-200 shadow-md p-10">
        <h1 className="text-xl"> Edit User</h1>

        <Field
          name="name"
          type="text"
          placeholder="name"
          className="input input-bordered"
        />
        <Field
          name="email"
          type="email"
          placeholder="email"
          className="input input-bordered"
        />
        <div className="flex gap-2 ">
          <ResetButton />
          <SaveButton />
        </div>
      </Form>
    </Formik>
  );
};

export default EditUserForm;
1234567891011121314151617181920212223242526272829303132333435

Scenario 3: Resetting the form when using useFormik hook #

If you're using useFormik to initialize your form, you can simply call resetFrom from the returned context. It's just preference but I don't like to use this method since you have to wire up the fields and form.

//EditUserFormHooks.tsx
import { Field, Form, Formik, useFormik } from "formik";
import React from "react";
import SaveButton from "./SaveButton";

const EditUserFormHooks = () => {
  //just a mock save function, let's pretend this calls the server
  const save = async () => {
    return {
      error: Math.random() > 0.5 ? "failed to save" : "",
    };
  };

  const formik = useFormik({
    onSubmit: async (values, helpers) => {
      const { error } = await save();

      if (!error) {
        helpers.resetForm({
          values,
        });
      }
    },
    initialValues: { name: "John Doe", email: "johndoe@gmail.com" },
  });

  return (
    <form
      className="flex flex-col gap-2 max-w-lg bg-base-200 shadow-md p-10"
      onSubmit={formik.submitForm}
    >
      <h1 className="text-xl"> Edit User</h1>

      <input
        name="name"
        type="text"
        placeholder="name"
        className="input input-bordered"
        onChange={formik.handleChange}
        value={formik.values.name}
      />
      <input
        name="email"
        type="email"
        placeholder="email"
        className="input input-bordered"
        onChange={formik.handleChange}
        value={formik.values.email}
      />
      <div className="flex gap-2 ">
        {/* check formik diry and reset form here */}
        <button
          type="button"
          className="btn btn-outline btn-error"
          disabled={!formik.dirty}
          onClick={() => formik.resetForm()}
        >
          Reset
        </button>
        <button
          type="submit"
          className="btn btn-primary"
          disabled={!formik.dirty}
        >
          Save
        </button>
      </div>
    </form>
  );
};

export default EditUserFormHooks;
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172

Final output #

Final output using formik state

Conclusion #

We've successfully reset our form using Formik by using <Formik>  provider or useFormik hook. Formik is very useful when managing forms in react! To learn more about formik you can check the docs here

If you like tutorials like this, please subscribe to our newsletter down below! If not, give it a like or share!

Credits: Image by Pete Linforth from Pixabay

Share this post!

Related Posts

Disclaimer

This content may contain links to products, software and services. Please assume all such links are affiliate links which may result in my earning commissions and fees.
As an Amazon Associate, I earn from qualifying purchases. This means that whenever you buy a product on Amazon from a link on our site, we receive a small percentage of its price at no extra cost to you. This helps us continue to provide valuable content and reviews to you. Thank you for your support!
Donate to ReactHustle