Formik Tutorial: Only Send Modified or Changed Fields on onSubmit.
Jasser Mark Arioste
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:
- Flatten the
initialValue
object, and create another object (resultObject
) to put the result. - Use
Object.entries()
to iterate to every entry. - Use
_.get()
to get thevalue
from the nested object and compare it to the value of the entry. - If the values are not equal, use
_.set(path, resultObject, modifiedValue)
- 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