ReactHustle

Formik Tutorial: Only Send Modified or Changed Fields on onSubmit.

Jasser Mark Arioste

Jasser Mark Arioste

Formik Tutorial: Only Send Modified or Changed Fields on onSubmit.

Hello, hustler! In this tutorial, you will learn how to send only the modified fields in Formik even when the initial value is a deeply nested object. 

The Problem #

Suppose you are building a CMS and implementing a feature to update a user record. You realize that passing in non-modified fields might not have the latest values. If there are many people editing a record, it could become a race condition and could lead to a loss of data.

As a best practice, you have to remove all the unchanged fields and retain all the modified fields. As a result one user will only update fields that he/she modified.

Suppose your initial value is this:

const initialValue = {
  first_name: "John",
  last_name: "Doe",
  address: {
    street: "Sesame",
    zipCode: 1234
  }
}
12345678

After submitting the form it changed to this:

{
  first_name: "Jane",
  last_name: "Doe",
  address: {
    street: "Sesame",
    zipCode: 6789
  }
}
12345678

The output that you want is this:

//only retain the modified values.
{
  first_name: "Jane",
  address: {
    zipCode: 6789
  }
}
1234567

It seems pretty straightforward, but is it? we need an algorithm!

The Algorithm #

Before coming up with this algorithm, I knew that I could use get and set methods from lodash. _.get gets the value using the path of an object. _.set sets the value using the path of the object.

For example, if you want to get the 'address.street' property, you can use _.get(initialValue, "address.street") and this will return the string "Sesame".

Similarly, you can use _.set(initialValue, "address.street", "Baker Street") to set the object's "address.street" property.

With this in mind, we only need to come up with get all the object paths. So that we can use lodash's get and set methods to compare values.

Here's the full algorithm:

  1. Flatten the initialValue object, and create another object (resultObject) to put the result.
  2. Use Object.entries() to iterate to every entry.
  3. Use _.get() to get the value from the nested object and compare it to the value of the entry.
  4. If the values are not equal, use _.set(path, resultObject, modifiedValue)
  5. After all entries have been checked, return the resultObject

Seems pretty confusing for now, so let's dive into the code.

Step 1: Flatten the Initial values #

Create a utility function to flatten an object:

export function flattenObject(ob: any) {
  var toReturn: any = {};

  for (var i in ob) {
    if (!ob.hasOwnProperty(i)) continue;

    if (typeof ob[i] == "object" && ob[i] !== null) {
      var flatObject = flattenObject(ob[i]);
      for (var x in flatObject) {
        if (!flatObject.hasOwnProperty(x)) continue;

        toReturn[i + "." + x] = flatObject[x];
      }
    } else {
      toReturn[i] = ob[i];
    }
  }
  return toReturn;
}
12345678910111213141516171819

This will flatten any object to a single-depth key-value pair. For example:

import { flattenObject } from "utils/flattenObject"

const initialValue = {
  first_name: "John",
  last_name: "Doe",
  address: {
    street: "Sesame"
    zipCode: 1234
  }
}

const flattenedObject = flattenObject(initialValue);
console.log(flattenedObject)
//{
//  first_name: "John",
//  last_name: "Doe",
//  "address.street": "Sesame",
//  "address.zipCode": 1234
// }
//}

1234567891011121314151617181920

Steps 2-5: Iterate through every entry and compare #

You should do this step on your onSubmit handler. First, you have to install lodash:

yarn add lodash
#or
npm install lodash
12

Next, you can use the algorithm:

import get from 'lodash/get'
import set from "lodash/set"
//formik onSubmit
const onSubmit = (values) => {
  const flattened = flattenObject(initialVal)
  //only get the modified values to not accidentally edit old ones.
  let resultObject: any = {}
  Object.entries(flattened)?.map(entry => {
    const [key, oldVal] = entry;
    const newVal = get(values, key)
    if (newVal !== oldVal) {
      set(resultObject, key, newVal)
    }
  })
  console.log(resultObject)
  //{
  //  first_name: "Jane",
  //  address: {
  //    zipCode: 6789
  //  }
  //}
}
12345678910111213141516171819202122

Explanation:

Line #8: We use Object.entries() to iterate through the flattened object's key-value pair.

Line #10: We use lodash.get to get the value from the updated fields.

Line #11-13: Compare old and new values. If they are not equal, use lodash.set to populate the resultObject.

That's pretty much it!

You can now use resultObject as the data for your backend API. By the way, I created a repo if you want to check the full code for this tutorial.

Conclusion #

We learned how to removed the unchanged values when submitting a form in Formik. I think this is especially useful when using MongoDB as your database. You can simply use the $set operator  paired with flattenObject function to only update the necessary fields.

If you like this tutorial, please leave a like or share this article. For future tutorials like this, please subscribe to our newsletter or follow me on Twitter.

Related Posts #

Credits: Image by Susanne Jutzeler, Schweiz, from Pixabay

Share this post!

Related Posts